@orkastery/cli 0.2.0
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.
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/adapters/README.md +22 -0
- package/adapters/claude-code/.claude-plugin/marketplace.json +15 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +31 -0
- package/adapters/claude-code/README.md +102 -0
- package/adapters/claude-code/agents/ork-check.md +32 -0
- package/adapters/claude-code/agents/ork-go.md +32 -0
- package/adapters/claude-code/agents/ork-goal.md +33 -0
- package/adapters/claude-code/agents/ork-master.md +32 -0
- package/adapters/claude-code/agents/ork-plan.md +32 -0
- package/adapters/claude-code/agents/ork-ship.md +32 -0
- package/adapters/claude-code/commands/check.md +45 -0
- package/adapters/claude-code/commands/go.md +46 -0
- package/adapters/claude-code/commands/goal.md +54 -0
- package/adapters/claude-code/commands/master.md +42 -0
- package/adapters/claude-code/commands/ork.md +37 -0
- package/adapters/claude-code/commands/plan.md +47 -0
- package/adapters/claude-code/commands/ship.md +45 -0
- package/adapters/claude-code/hooks/hooks.json +17 -0
- package/adapters/claude-code/hooks/ork-guard.js +130 -0
- package/adapters/hermes/README.md +45 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +25 -0
- package/adapters/hermes/hermes.plugin.json +21 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +116 -0
- package/adapters/openclaw/README.md +63 -0
- package/adapters/openclaw/bin/ork-abrir-thread.sh +24 -0
- package/adapters/openclaw/openclaw.plugin.json +138 -0
- package/dist/adapters/claude-bg.js +308 -0
- package/dist/auditoria.js +848 -0
- package/dist/auditrun.js +976 -0
- package/dist/board.js +314 -0
- package/dist/canarios.js +271 -0
- package/dist/catalogo.js +124 -0
- package/dist/ciclos.js +156 -0
- package/dist/claims.js +226 -0
- package/dist/divida.js +374 -0
- package/dist/doctor.js +246 -0
- package/dist/evalrunner.js +458 -0
- package/dist/fix.js +450 -0
- package/dist/gates.js +92 -0
- package/dist/handoff.js +521 -0
- package/dist/hosts.js +331 -0
- package/dist/index.js +1910 -0
- package/dist/init.js +231 -0
- package/dist/leases.js +508 -0
- package/dist/ledger.js +138 -0
- package/dist/manifest.js +255 -0
- package/dist/master.js +453 -0
- package/dist/memoria.js +773 -0
- package/dist/modos.js +220 -0
- package/dist/orkmind.js +487 -0
- package/dist/orquestracao.js +559 -0
- package/dist/phase.js +572 -0
- package/dist/policies.js +176 -0
- package/dist/prompts.js +406 -0
- package/dist/ratelimit.js +179 -0
- package/dist/recall.js +252 -0
- package/dist/retry.js +917 -0
- package/dist/sandbox.js +94 -0
- package/dist/sessoes.js +104 -0
- package/dist/ship.js +551 -0
- package/dist/slug.js +100 -0
- package/dist/superficie.js +1347 -0
- package/dist/thread.js +333 -0
- package/dist/tokens.js +228 -0
- package/dist/types.js +20 -0
- package/dist/util.js +156 -0
- package/dist/verify.js +262 -0
- package/dist/worktree.js +576 -0
- package/dist/yaml.js +112 -0
- package/eval/README.md +85 -0
- package/eval/casos/check-quality.json +103 -0
- package/eval/casos/code-reviewer.json +102 -0
- package/eval/casos/decision-triage.json +103 -0
- package/eval/casos/go-implementation.json +103 -0
- package/eval/casos/goal-definition.json +103 -0
- package/eval/casos/master-metrics.json +103 -0
- package/eval/casos/narrative-guardian.json +163 -0
- package/eval/casos/orkastery-bootstrap.json +102 -0
- package/eval/casos/plan-specification.json +102 -0
- package/eval/casos/roadmap-keeper.json +103 -0
- package/eval/casos/scope-check-capability-map.json +83 -0
- package/eval/casos/security-auditor.json +102 -0
- package/eval/casos/ship-release.json +122 -0
- package/eval/casos/test-engineer.json +122 -0
- package/eval/casos/thread-state.json +82 -0
- package/eval/casos/thread-tracing.json +83 -0
- package/eval/casos/web-performance-auditor.json +102 -0
- package/eval/fixtures/b0-slug-e-modos/caso.json +56 -0
- package/eval/fixtures/b2-master-log/caso.json +91 -0
- package/eval/fixtures/fx-concurrency/caso.json +15 -0
- package/eval/fixtures/fx-hallucination/caso.json +12 -0
- package/eval/fixtures/fx-happy/caso.json +16 -0
- package/eval/fixtures/fx-schema-drift/caso.json +15 -0
- package/eval/fixtures/fx-stale-base/caso.json +12 -0
- package/eval/fixtures/fx-wiki-destroy/caso.json +16 -0
- package/eval/fixtures/superficie-de-rede/README.md +22 -0
- package/eval/fixtures/superficie-de-rede/api-express.js +30 -0
- package/eval/fixtures/superficie-de-rede/api_fastapi.py +17 -0
- package/eval/fixtures/superficie-de-rede/api_gin.go +18 -0
- package/package.json +56 -0
- package/references/README.md +25 -0
- package/references/code-review-axes.md +82 -0
- package/references/definition-of-done.md +74 -0
- package/references/performance-checklist.md +53 -0
- package/references/security-checklist.md +101 -0
- package/references/testing-patterns.md +51 -0
- package/skills/README.md +43 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +89 -0
- package/skills/core/thread-state/SKILL.md +86 -0
- package/skills/governance/decision-triage/SKILL.md +65 -0
- package/skills/governance/narrative-guardian/SKILL.md +66 -0
- package/skills/governance/roadmap-keeper/SKILL.md +60 -0
- package/skills/governance/scope-check-capability-map/SKILL.md +61 -0
- package/skills/observability/thread-tracing/SKILL.md +63 -0
- package/skills/phases/check-quality/SKILL.md +87 -0
- package/skills/phases/go-implementation/SKILL.md +70 -0
- package/skills/phases/goal-definition/SKILL.md +69 -0
- package/skills/phases/master-metrics/SKILL.md +65 -0
- package/skills/phases/plan-specification/SKILL.md +67 -0
- package/skills/phases/ship-release/SKILL.md +68 -0
- package/skills/reviewers/code-reviewer/SKILL.md +61 -0
- package/skills/reviewers/security-auditor/SKILL.md +68 -0
- package/skills/reviewers/test-engineer/SKILL.md +66 -0
- package/skills/reviewers/web-performance-auditor/SKILL.md +63 -0
package/dist/master.js
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Modulo MASTER/score: o coracao do bloco B2.
|
|
4
|
+
*
|
|
5
|
+
* "N looping threads, 1 score." A fase MASTER fecha toda thread, em todo modo, com
|
|
6
|
+
* duas pecas que o `ork` grava e valida:
|
|
7
|
+
*
|
|
8
|
+
* - `POSTMORTEM.json`: o corpo estruturado, com CLASSES DE FALHA FIXAS. Classe livre
|
|
9
|
+
* vira texto solto, e texto solto nao agrega no `ork learn` do B4.
|
|
10
|
+
* - `master-log.json`: o MASTER log no CONTRATO CONGELADO. Os nomes dos campos sao o
|
|
11
|
+
* contrato e nao mudam. "Uma entrega sem MASTER log nao aconteceu."
|
|
12
|
+
*
|
|
13
|
+
* O score e humano, de 0 a 5, com JUSTIFICATIVA OBRIGATORIA: o `ork` recusa score sem
|
|
14
|
+
* justificativa, em qualquer modo. Nos modos que nao pausam no MASTER (`#Ork`,
|
|
15
|
+
* `#Maestro`, `#Auto`) o score nao desaparece: a thread cai na fila de batch scoring,
|
|
16
|
+
* que `ork master --batch` lista para o humano pontuar em bloco.
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.ORDEM_DAS_CLASSES = exports.CLASSES_DE_FALHA = exports.VERSAO_MASTER_LOG = exports.CONTRATO_MASTER_LOG = void 0;
|
|
53
|
+
exports.parseClasse = parseClasse;
|
|
54
|
+
exports.caminhoPostmortem = caminhoPostmortem;
|
|
55
|
+
exports.caminhoMasterLog = caminhoMasterLog;
|
|
56
|
+
exports.fasesPercorridas = fasesPercorridas;
|
|
57
|
+
exports.regimeDoScore = regimeDoScore;
|
|
58
|
+
exports.entregou = entregou;
|
|
59
|
+
exports.exigirScoreValido = exigirScoreValido;
|
|
60
|
+
exports.registrarMaster = registrarMaster;
|
|
61
|
+
exports.validarMasterLog = validarMasterLog;
|
|
62
|
+
exports.lerMasterLog = lerMasterLog;
|
|
63
|
+
exports.pendentesDeScore = pendentesDeScore;
|
|
64
|
+
exports.tabelaDeBatch = tabelaDeBatch;
|
|
65
|
+
exports.textoDoMaster = textoDoMaster;
|
|
66
|
+
exports.tabelaDeClasses = tabelaDeClasses;
|
|
67
|
+
const path = __importStar(require("node:path"));
|
|
68
|
+
const ledger_1 = require("./ledger");
|
|
69
|
+
const modos_1 = require("./modos");
|
|
70
|
+
const thread_1 = require("./thread");
|
|
71
|
+
const types_1 = require("./types");
|
|
72
|
+
const util_1 = require("./util");
|
|
73
|
+
const fs = __importStar(require("node:fs"));
|
|
74
|
+
/** Identificador do contrato congelado do MASTER log. */
|
|
75
|
+
exports.CONTRATO_MASTER_LOG = 'ork.master-log/v1';
|
|
76
|
+
/** Versao do contrato. Uma mudanca de contrato exige uma versao nova, nao um campo novo. */
|
|
77
|
+
exports.VERSAO_MASTER_LOG = 1;
|
|
78
|
+
/** As classes de falha FIXAS do POSTMORTEM, com o que cada uma significa. */
|
|
79
|
+
exports.CLASSES_DE_FALHA = {
|
|
80
|
+
'sem-falha': 'a thread atravessou o ciclo sem incidente',
|
|
81
|
+
'erro-de-spec': 'o objetivo ou o plano estavam errados; a execucao seguiu a spec errada',
|
|
82
|
+
'base-avancou': 'a base mudou embaixo da thread e o trabalho precisou ser ressincronizado',
|
|
83
|
+
conflito: 'colisao de regiao entre threads, resolvida na mao',
|
|
84
|
+
'rate-limit': 'o runtime bateu limite de uso e a fase parou por isso',
|
|
85
|
+
modelo: 'o modelo errou (alucinou, ignorou instrucao, entregou self-report)',
|
|
86
|
+
processo: 'o metodo falhou: gate faltando, verificacao pulada, evidencia nao coletada',
|
|
87
|
+
'scope-creep': 'a entrega cresceu alem do que o GOAL prometia',
|
|
88
|
+
outra: 'nao cabe em nenhuma classe acima (descreva na justificativa)',
|
|
89
|
+
};
|
|
90
|
+
/** Ordem canonica das classes na saida do CLI. */
|
|
91
|
+
exports.ORDEM_DAS_CLASSES = [
|
|
92
|
+
'sem-falha',
|
|
93
|
+
'erro-de-spec',
|
|
94
|
+
'base-avancou',
|
|
95
|
+
'conflito',
|
|
96
|
+
'rate-limit',
|
|
97
|
+
'modelo',
|
|
98
|
+
'processo',
|
|
99
|
+
'scope-creep',
|
|
100
|
+
'outra',
|
|
101
|
+
];
|
|
102
|
+
/** Remove acentos: a classe escrita com acento vira a classe fixa equivalente. */
|
|
103
|
+
function semAcento(texto) {
|
|
104
|
+
return texto.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
|
|
105
|
+
}
|
|
106
|
+
/** Parseia uma classe de falha. Retorna null quando ela nao esta no catalogo fixo. */
|
|
107
|
+
function parseClasse(bruto) {
|
|
108
|
+
if (!bruto)
|
|
109
|
+
return null;
|
|
110
|
+
const limpo = semAcento(bruto.trim().toLowerCase()).replace(/[\s_]+/g, '-');
|
|
111
|
+
return exports.ORDEM_DAS_CLASSES.includes(limpo)
|
|
112
|
+
? limpo
|
|
113
|
+
: null;
|
|
114
|
+
}
|
|
115
|
+
/** Caminho do POSTMORTEM da thread. */
|
|
116
|
+
function caminhoPostmortem(raiz, id) {
|
|
117
|
+
return path.join((0, thread_1.dirThread)(raiz, id), 'POSTMORTEM.json');
|
|
118
|
+
}
|
|
119
|
+
/** Caminho do MASTER log da thread. */
|
|
120
|
+
function caminhoMasterLog(raiz, id) {
|
|
121
|
+
return path.join((0, thread_1.dirThread)(raiz, id), 'master-log.json');
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* As fases que a thread realmente percorreu, reconstruidas do LEDGER.
|
|
125
|
+
*
|
|
126
|
+
* Nao e o que o modo prometia nem o que a sessao disse ter feito: e o que ficou
|
|
127
|
+
* registrado, com quantos eventos e em que janela de tempo.
|
|
128
|
+
*/
|
|
129
|
+
function fasesPercorridas(raiz, id) {
|
|
130
|
+
const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, id));
|
|
131
|
+
const porFase = new Map();
|
|
132
|
+
for (const e of eventos) {
|
|
133
|
+
const bruta = typeof e.fase === 'string' ? e.fase.toUpperCase() : null;
|
|
134
|
+
if (!bruta || !types_1.FASES.includes(bruta))
|
|
135
|
+
continue;
|
|
136
|
+
const atual = porFase.get(bruta) ?? {
|
|
137
|
+
fase: bruta,
|
|
138
|
+
eventos: 0,
|
|
139
|
+
primeiroEm: e.ts,
|
|
140
|
+
ultimoEm: e.ts,
|
|
141
|
+
sessoes: [],
|
|
142
|
+
};
|
|
143
|
+
atual.eventos += 1;
|
|
144
|
+
atual.ultimoEm = e.ts;
|
|
145
|
+
const slug = typeof e.slug === 'string' ? e.slug : null;
|
|
146
|
+
if (slug && !atual.sessoes.includes(slug))
|
|
147
|
+
atual.sessoes.push(slug);
|
|
148
|
+
porFase.set(bruta, atual);
|
|
149
|
+
}
|
|
150
|
+
return types_1.FASES.filter((f) => porFase.has(f)).map((f) => porFase.get(f));
|
|
151
|
+
}
|
|
152
|
+
/** Regime do score: `pausa` nos modos que param no MASTER, `batch` nos demais. */
|
|
153
|
+
function regimeDoScore(thread) {
|
|
154
|
+
return (0, thread_1.pausaNaThread)(thread, 'MASTER') ? 'pausa' : 'batch';
|
|
155
|
+
}
|
|
156
|
+
/** A thread ja entregou (o ledger tem `ship_done`)? */
|
|
157
|
+
function entregou(raiz, id) {
|
|
158
|
+
return (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, id)).some((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Valida o score humano ANTES de gravar qualquer coisa.
|
|
162
|
+
*
|
|
163
|
+
* Score sem justificativa nao e score, e carimbo. Por isso o `ork` recusa, e recusa
|
|
164
|
+
* igual em todos os modos: o modo afrouxa a pausa, nunca a verificacao.
|
|
165
|
+
*/
|
|
166
|
+
function exigirScoreValido(score, justificativa) {
|
|
167
|
+
if (!Number.isInteger(score) || score < 0 || score > 5) {
|
|
168
|
+
throw new Error(`score invalido: "${score}". O score do MASTER e um inteiro de 0 a 5 (--score 0..5)`);
|
|
169
|
+
}
|
|
170
|
+
if (justificativa.trim().length < 3) {
|
|
171
|
+
throw new Error('score sem justificativa: o MASTER exige --justificativa "<texto>". ' +
|
|
172
|
+
'Score sem justificativa nao vira aprendizado e o `ork` nao o registra.');
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Registra o MASTER da thread: POSTMORTEM tipado, MASTER log no contrato e o score.
|
|
177
|
+
* Ao fim, a thread fecha (`status: fechada`) e o ledger ganha `master_done`.
|
|
178
|
+
*/
|
|
179
|
+
function registrarMaster(raiz, id, opcoes) {
|
|
180
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
181
|
+
exigirScoreValido(opcoes.score, opcoes.justificativa);
|
|
182
|
+
if (thread.score && !opcoes.refazer) {
|
|
183
|
+
throw new Error(`a thread ${id} ja tem score ${thread.score.valor}/5 dado por ${thread.score.avaliadoPor} ` +
|
|
184
|
+
`em ${thread.score.avaliadoEm}; use --refazer para regravar o MASTER`);
|
|
185
|
+
}
|
|
186
|
+
const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, id));
|
|
187
|
+
const gatesBloqueados = eventos
|
|
188
|
+
.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.gateBloqueado)
|
|
189
|
+
.map((e) => ({
|
|
190
|
+
ts: e.ts,
|
|
191
|
+
motivo: String(e.motivo ?? '(sem motivo)'),
|
|
192
|
+
detalhe: String(e.detalhe ?? ''),
|
|
193
|
+
}));
|
|
194
|
+
const entregas = eventos
|
|
195
|
+
.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.shipConcluido)
|
|
196
|
+
.map((e) => ({
|
|
197
|
+
ts: e.ts,
|
|
198
|
+
de: String(e.de ?? ''),
|
|
199
|
+
para: String(e.para ?? ''),
|
|
200
|
+
mergeSha: String(e.mergeSha ?? ''),
|
|
201
|
+
pushVerificado: e.pushVerificado === true,
|
|
202
|
+
}));
|
|
203
|
+
const avisos = [];
|
|
204
|
+
let classesInferidas = false;
|
|
205
|
+
let classes = opcoes.classes ?? [];
|
|
206
|
+
if (classes.length === 0) {
|
|
207
|
+
classesInferidas = true;
|
|
208
|
+
classes = gatesBloqueados.length > 0 ? ['outra'] : ['sem-falha'];
|
|
209
|
+
avisos.push(gatesBloqueados.length > 0
|
|
210
|
+
? `a thread levou ${gatesBloqueados.length} reprovacao(oes) tipada(s) e nenhuma --classe foi ` +
|
|
211
|
+
'informada: o `ork` gravou "outra". Corrija com --classe <classe> --refazer.'
|
|
212
|
+
: 'nenhuma --classe informada e nenhum gate reprovou: o `ork` gravou "sem-falha".');
|
|
213
|
+
}
|
|
214
|
+
const score = {
|
|
215
|
+
valor: opcoes.score,
|
|
216
|
+
justificativa: opcoes.justificativa.trim(),
|
|
217
|
+
avaliadoPor: (opcoes.por ?? 'humano').trim() || 'humano',
|
|
218
|
+
avaliadoEm: (0, util_1.agora)(),
|
|
219
|
+
regime: regimeDoScore(thread),
|
|
220
|
+
};
|
|
221
|
+
const percorridas = fasesPercorridas(raiz, id);
|
|
222
|
+
const resumo = opcoes.resumo?.trim() ||
|
|
223
|
+
`thread ${thread.id} (${(0, modos_1.tagDoModo)(thread.modo)}) fechada com score ${score.valor}/5 ` +
|
|
224
|
+
`apos ${percorridas.length} fase(s) registradas no ledger e ${entregas.length} entrega(s)`;
|
|
225
|
+
const postmortem = {
|
|
226
|
+
versao: exports.VERSAO_MASTER_LOG,
|
|
227
|
+
thread: thread.id,
|
|
228
|
+
slug: thread.slug,
|
|
229
|
+
modo: thread.modo,
|
|
230
|
+
tag: (0, modos_1.tagDoModo)(thread.modo),
|
|
231
|
+
variante: thread.variante ?? null,
|
|
232
|
+
fasesPercorridas: percorridas,
|
|
233
|
+
classesDeFalha: classes,
|
|
234
|
+
score,
|
|
235
|
+
gatesBloqueados,
|
|
236
|
+
decisoesAutonomas: eventos.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.decisaoAutonoma).length,
|
|
237
|
+
pausasHumanas: eventos.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.pausaHumana).length,
|
|
238
|
+
entregas,
|
|
239
|
+
resumo,
|
|
240
|
+
geradoEm: (0, util_1.agora)(),
|
|
241
|
+
};
|
|
242
|
+
const masterLog = {
|
|
243
|
+
contrato: exports.CONTRATO_MASTER_LOG,
|
|
244
|
+
versao: exports.VERSAO_MASTER_LOG,
|
|
245
|
+
thread: thread.id,
|
|
246
|
+
slug: thread.slug,
|
|
247
|
+
projeto: thread.projeto,
|
|
248
|
+
modo: thread.modo,
|
|
249
|
+
tag: (0, modos_1.tagDoModo)(thread.modo),
|
|
250
|
+
variante: thread.variante ?? null,
|
|
251
|
+
// Fases PERCORRIDAS, nao as prometidas pelo modo. Ledger vazio vira ['MASTER'], que e
|
|
252
|
+
// a unica fase que de fato aconteceu: dizer o contrario seria self-report.
|
|
253
|
+
fases: percorridas.length > 0 ? percorridas.map((f) => f.fase) : ['MASTER'],
|
|
254
|
+
score: score.valor,
|
|
255
|
+
justificativa: score.justificativa,
|
|
256
|
+
avaliadoPor: score.avaliadoPor,
|
|
257
|
+
avaliadoEm: score.avaliadoEm,
|
|
258
|
+
classesDeFalha: classes,
|
|
259
|
+
resumo,
|
|
260
|
+
base: thread.base,
|
|
261
|
+
worktree: thread.worktree,
|
|
262
|
+
evidencia: {
|
|
263
|
+
ledger: path.join('.orkastery', 'threads', thread.id, 'ledger.jsonl'),
|
|
264
|
+
postmortem: path.join('.orkastery', 'threads', thread.id, 'POSTMORTEM.json'),
|
|
265
|
+
eventos: eventos.length,
|
|
266
|
+
sessoes: thread.sessoes.length,
|
|
267
|
+
claims: thread.claims.length,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
const invalido = validarMasterLog(masterLog);
|
|
271
|
+
if (invalido.length > 0) {
|
|
272
|
+
throw new Error(`MASTER log fora do contrato congelado:\n - ${invalido.join('\n - ')}`);
|
|
273
|
+
}
|
|
274
|
+
const caminhoP = caminhoPostmortem(raiz, id);
|
|
275
|
+
const caminhoM = caminhoMasterLog(raiz, id);
|
|
276
|
+
(0, util_1.gravarJson)(caminhoP, postmortem);
|
|
277
|
+
(0, util_1.gravarJson)(caminhoM, masterLog);
|
|
278
|
+
thread.score = score;
|
|
279
|
+
thread.faseAtual = 'MASTER';
|
|
280
|
+
thread.status = 'fechada';
|
|
281
|
+
(0, thread_1.gravarThread)(raiz, thread);
|
|
282
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.postmortemGravado, {
|
|
283
|
+
fase: 'MASTER',
|
|
284
|
+
classesDeFalha: classes,
|
|
285
|
+
classesInferidas,
|
|
286
|
+
gatesBloqueados: gatesBloqueados.length,
|
|
287
|
+
arquivo: path.relative(raiz, caminhoP),
|
|
288
|
+
});
|
|
289
|
+
(0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, id), id, ledger_1.TIPOS_DE_EVENTO.masterConcluido, {
|
|
290
|
+
fase: 'MASTER',
|
|
291
|
+
modo: thread.modo,
|
|
292
|
+
tag: (0, modos_1.tagDoModo)(thread.modo),
|
|
293
|
+
score: score.valor,
|
|
294
|
+
justificativa: score.justificativa,
|
|
295
|
+
autorizadoPor: score.avaliadoPor,
|
|
296
|
+
regime: score.regime,
|
|
297
|
+
contrato: exports.CONTRATO_MASTER_LOG,
|
|
298
|
+
arquivo: path.relative(raiz, caminhoM),
|
|
299
|
+
estado: 'thread fechada',
|
|
300
|
+
});
|
|
301
|
+
return {
|
|
302
|
+
thread,
|
|
303
|
+
postmortem,
|
|
304
|
+
masterLog,
|
|
305
|
+
caminhoPostmortem: caminhoP,
|
|
306
|
+
caminhoMasterLog: caminhoM,
|
|
307
|
+
classesInferidas,
|
|
308
|
+
avisos,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Valida um MASTER log contra o contrato congelado. Devolve a lista de erros (vazia = valido).
|
|
313
|
+
* E o mesmo validador que a suite roda: o contrato e executavel, nao prosa.
|
|
314
|
+
*/
|
|
315
|
+
function validarMasterLog(bruto) {
|
|
316
|
+
const erros = [];
|
|
317
|
+
if (!bruto || typeof bruto !== 'object')
|
|
318
|
+
return ['o MASTER log nao e um objeto'];
|
|
319
|
+
const log = bruto;
|
|
320
|
+
if (log.contrato !== exports.CONTRATO_MASTER_LOG) {
|
|
321
|
+
erros.push(`campo "contrato" deve ser "${exports.CONTRATO_MASTER_LOG}"`);
|
|
322
|
+
}
|
|
323
|
+
if (log.versao !== exports.VERSAO_MASTER_LOG)
|
|
324
|
+
erros.push(`campo "versao" deve ser ${exports.VERSAO_MASTER_LOG}`);
|
|
325
|
+
for (const campo of ['thread', 'slug', 'modo', 'tag', 'justificativa', 'resumo', 'avaliadoPor']) {
|
|
326
|
+
const v = log[campo];
|
|
327
|
+
if (typeof v !== 'string' || v.trim() === '')
|
|
328
|
+
erros.push(`campo "${campo}" ausente ou vazio`);
|
|
329
|
+
}
|
|
330
|
+
if (typeof log.score !== 'number' || !Number.isInteger(log.score) || log.score < 0 || log.score > 5) {
|
|
331
|
+
erros.push('campo "score" deve ser um inteiro de 0 a 5');
|
|
332
|
+
}
|
|
333
|
+
if (typeof log.avaliadoEm !== 'string' || Number.isNaN(Date.parse(log.avaliadoEm))) {
|
|
334
|
+
erros.push('campo "avaliadoEm" deve ser um carimbo ISO valido');
|
|
335
|
+
}
|
|
336
|
+
if (!Array.isArray(log.fases) || log.fases.length === 0) {
|
|
337
|
+
erros.push('campo "fases" deve listar as fases percorridas');
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
const fora = log.fases.filter((f) => !types_1.FASES.includes(f));
|
|
341
|
+
if (fora.length > 0)
|
|
342
|
+
erros.push(`campo "fases" tem fase fora do ciclo canonico: ${fora.join(', ')}`);
|
|
343
|
+
}
|
|
344
|
+
if (!Array.isArray(log.classesDeFalha) || log.classesDeFalha.length === 0) {
|
|
345
|
+
erros.push('campo "classesDeFalha" deve ter ao menos uma classe fixa');
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
const fora = log.classesDeFalha.filter((c) => !exports.ORDEM_DAS_CLASSES.includes(c));
|
|
349
|
+
if (fora.length > 0) {
|
|
350
|
+
erros.push(`campo "classesDeFalha" tem classe fora do catalogo fixo: ${fora.join(', ')}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (!log.projeto || typeof log.projeto.name !== 'string' || typeof log.projeto.abbrev !== 'string') {
|
|
354
|
+
erros.push('campo "projeto" precisa de name e abbrev');
|
|
355
|
+
}
|
|
356
|
+
if (!log.base || typeof log.base.branch !== 'string' || typeof log.base.commit !== 'string') {
|
|
357
|
+
erros.push('campo "base" precisa de branch e commit');
|
|
358
|
+
}
|
|
359
|
+
if (!log.evidencia || typeof log.evidencia.ledger !== 'string' || typeof log.evidencia.postmortem !== 'string') {
|
|
360
|
+
erros.push('campo "evidencia" precisa apontar o ledger e o POSTMORTEM');
|
|
361
|
+
}
|
|
362
|
+
return erros;
|
|
363
|
+
}
|
|
364
|
+
/** Le o MASTER log de uma thread, quando existe. */
|
|
365
|
+
function lerMasterLog(raiz, id) {
|
|
366
|
+
const caminho = caminhoMasterLog(raiz, id);
|
|
367
|
+
if (!fs.existsSync(caminho))
|
|
368
|
+
return null;
|
|
369
|
+
return (0, util_1.lerJson)(caminho);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* A fila de batch scoring: threads que entregaram (ou chegaram ao MASTER) e ainda nao
|
|
373
|
+
* tem score. E a fila que `#Ork`, `#Maestro` e `#Auto` alimentam ao entregar sem pausa.
|
|
374
|
+
*/
|
|
375
|
+
function pendentesDeScore(raiz, todas = false) {
|
|
376
|
+
const saida = [];
|
|
377
|
+
for (const id of (0, thread_1.listarIds)(raiz)) {
|
|
378
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
379
|
+
if (thread.score)
|
|
380
|
+
continue;
|
|
381
|
+
const jaEntregou = entregou(raiz, id);
|
|
382
|
+
const noMaster = thread.faseAtual === 'MASTER';
|
|
383
|
+
if (!todas && !jaEntregou && !noMaster)
|
|
384
|
+
continue;
|
|
385
|
+
saida.push({
|
|
386
|
+
thread,
|
|
387
|
+
regime: regimeDoScore(thread),
|
|
388
|
+
entregou: jaEntregou,
|
|
389
|
+
noMaster,
|
|
390
|
+
fasesPercorridas: fasesPercorridas(raiz, id).length,
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return saida;
|
|
394
|
+
}
|
|
395
|
+
/** Tabela de `ork master --batch`: o review em bloco. */
|
|
396
|
+
function tabelaDeBatch(raiz, todas = false) {
|
|
397
|
+
const pendentes = pendentesDeScore(raiz, todas);
|
|
398
|
+
if (pendentes.length === 0) {
|
|
399
|
+
return todas
|
|
400
|
+
? 'Nenhuma thread aberta sem score. A fila de batch scoring esta vazia.'
|
|
401
|
+
: 'Nenhuma thread entregue esperando score. A fila de batch scoring esta vazia.\n' +
|
|
402
|
+
'Para ver tambem as threads que ainda nao entregaram: ork master --batch --todas';
|
|
403
|
+
}
|
|
404
|
+
const linhas = pendentes.map((p) => [
|
|
405
|
+
p.thread.id,
|
|
406
|
+
p.thread.slug,
|
|
407
|
+
(0, modos_1.tagDoModo)(p.thread.modo),
|
|
408
|
+
p.thread.faseAtual,
|
|
409
|
+
p.thread.status,
|
|
410
|
+
p.regime,
|
|
411
|
+
p.entregou ? 'sim' : 'nao',
|
|
412
|
+
String(p.fasesPercorridas),
|
|
413
|
+
]);
|
|
414
|
+
const cabecalho = (0, util_1.tabela)(['ID', 'SLUG', 'MODO', 'FASE', 'STATUS', 'REGIME', 'ENTREGOU', 'FASES'], linhas);
|
|
415
|
+
return [
|
|
416
|
+
`Fila de score (${pendentes.length} thread(s) esperando o humano)`,
|
|
417
|
+
'',
|
|
418
|
+
cabecalho,
|
|
419
|
+
'',
|
|
420
|
+
'Pontue em bloco, uma linha por thread:',
|
|
421
|
+
...pendentes.map((p) => ` ork master ${p.thread.id} --score <0-5> --justificativa "<por que>" --classe <classe>`),
|
|
422
|
+
'',
|
|
423
|
+
`Classes fixas: ${exports.ORDEM_DAS_CLASSES.join(', ')}`,
|
|
424
|
+
].join('\n');
|
|
425
|
+
}
|
|
426
|
+
/** Texto de `ork master <thread-id> --score`. */
|
|
427
|
+
function textoDoMaster(r) {
|
|
428
|
+
const linhas = [];
|
|
429
|
+
linhas.push(`MASTER registrado: thread ${r.thread.id} fechada.`);
|
|
430
|
+
linhas.push(` score ${r.masterLog.score}/5 (${r.postmortem.score.regime})`);
|
|
431
|
+
linhas.push(` justificativa ${r.masterLog.justificativa}`);
|
|
432
|
+
linhas.push(` avaliado por ${r.masterLog.avaliadoPor} em ${r.masterLog.avaliadoEm}`);
|
|
433
|
+
linhas.push(` classes ${r.masterLog.classesDeFalha.join(', ')}`);
|
|
434
|
+
linhas.push(` fases ${r.masterLog.fases.join(' -> ')}`);
|
|
435
|
+
linhas.push(` entregas ${r.postmortem.entregas.length} ship(s) concluido(s) no ledger`);
|
|
436
|
+
linhas.push(` gates ${r.postmortem.gatesBloqueados.length} reprovacao(oes) tipada(s)`);
|
|
437
|
+
linhas.push('');
|
|
438
|
+
linhas.push(` POSTMORTEM ${r.caminhoPostmortem}`);
|
|
439
|
+
linhas.push(` MASTER log ${r.caminhoMasterLog} (contrato ${r.masterLog.contrato})`);
|
|
440
|
+
if (r.avisos.length > 0) {
|
|
441
|
+
linhas.push('');
|
|
442
|
+
for (const a of r.avisos)
|
|
443
|
+
linhas.push(` AVISO: ${a}`);
|
|
444
|
+
}
|
|
445
|
+
return linhas.join('\n');
|
|
446
|
+
}
|
|
447
|
+
/** Tabela das classes de falha fixas, para o humano escolher sem inventar. */
|
|
448
|
+
function tabelaDeClasses() {
|
|
449
|
+
const linhas = ['Classes de falha do POSTMORTEM (fixas, nao extensiveis pelo executor)', ''];
|
|
450
|
+
for (const c of exports.ORDEM_DAS_CLASSES)
|
|
451
|
+
linhas.push(` ${c.padEnd(14)} ${exports.CLASSES_DE_FALHA[c]}`);
|
|
452
|
+
return linhas.join('\n');
|
|
453
|
+
}
|