@orkastery/cli 0.6.1 → 0.6.2

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.
Files changed (69) hide show
  1. package/adapters/claude-code/agents/ork-check.md +1 -1
  2. package/adapters/claude-code/agents/ork-go.md +1 -1
  3. package/adapters/claude-code/agents/ork-goal.md +1 -1
  4. package/adapters/claude-code/agents/ork-master.md +1 -1
  5. package/adapters/claude-code/agents/ork-plan.md +1 -1
  6. package/adapters/claude-code/agents/ork-ship.md +1 -1
  7. package/adapters/claude-code/commands/ork.md +1 -1
  8. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +2 -0
  9. package/adapters/codex/skills/ork/SKILL.md +1 -1
  10. package/adapters/openclaw/openclaw.plugin.json +1 -1
  11. package/adapters/openclaw/package.json +1 -1
  12. package/assets/adapters-en/claude-code/agents/ork-check.md +1 -1
  13. package/assets/adapters-en/claude-code/agents/ork-go.md +1 -1
  14. package/assets/adapters-en/claude-code/agents/ork-goal.md +1 -1
  15. package/assets/adapters-en/claude-code/agents/ork-master.md +1 -1
  16. package/assets/adapters-en/claude-code/agents/ork-plan.md +1 -1
  17. package/assets/adapters-en/claude-code/agents/ork-ship.md +1 -1
  18. package/assets/adapters-en/claude-code/commands/ork.md +1 -1
  19. package/assets/adapters-en/codex/skills/ork/SKILL.md +1 -1
  20. package/assets/orkmind_fixture.py +4 -1
  21. package/assets/skills-en/core/orkastery-bootstrap/SKILL.md +1 -1
  22. package/assets/skills-en/phases/check-quality/SKILL.md +1 -1
  23. package/assets/skills-en/phases/go-implementation/SKILL.md +1 -1
  24. package/dist/adapters/claude-bg.js +3 -1
  25. package/dist/adapters/grok-mcp-servidor.js +22 -12
  26. package/dist/adapters/grok.js +1 -1
  27. package/dist/changelog-check.js +217 -0
  28. package/dist/check-agil.js +241 -5
  29. package/dist/ci.js +3 -2
  30. package/dist/claim-lint.js +29 -0
  31. package/dist/claims.js +4 -0
  32. package/dist/docs.js +7 -0
  33. package/dist/fila-pesada.js +121 -11
  34. package/dist/hitl-classificacao.js +1 -0
  35. package/dist/index.js +43 -25
  36. package/dist/intelligence-graph-cli.js +27 -9
  37. package/dist/locale-claims.js +16 -2
  38. package/dist/locale-grafo.js +56 -1
  39. package/dist/locale-maestro.js +38 -0
  40. package/dist/locale-network.js +8 -6
  41. package/dist/locale-onboarding.js +14 -0
  42. package/dist/locale-preflight.js +80 -0
  43. package/dist/locale-projetos.js +6 -0
  44. package/dist/locale-prompts.js +8 -8
  45. package/dist/locale-pulse.js +4 -0
  46. package/dist/locale-retry.js +16 -0
  47. package/dist/locale-security-scan.js +35 -1
  48. package/dist/locale-verify.js +15 -0
  49. package/dist/locale.js +24 -7
  50. package/dist/maestro-cli.js +12 -17
  51. package/dist/mcp-install.js +2 -1
  52. package/dist/mcp-server.js +20 -17
  53. package/dist/mcp-verify-async.js +353 -0
  54. package/dist/mcp-verify-worker.js +17 -0
  55. package/dist/mcp-verify.js +10 -1
  56. package/dist/pedido-conducao.js +51 -0
  57. package/dist/preflight.js +25 -26
  58. package/dist/prompts.js +4 -2
  59. package/dist/pulse.js +4 -2
  60. package/dist/rede.js +24 -12
  61. package/dist/retry.js +12 -0
  62. package/dist/security-scan/cli.js +2 -2
  63. package/dist/security-scan/report.js +1 -1
  64. package/dist/session-watcher.js +33 -7
  65. package/dist/verify.js +166 -34
  66. package/package.json +1 -1
  67. package/skills/core/orkastery-bootstrap/SKILL.md +1 -1
  68. package/skills/phases/check-quality/SKILL.md +1 -1
  69. package/skills/phases/go-implementation/SKILL.md +1 -1
@@ -0,0 +1,353 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.CONTRATO_VERIFY_MCP = void 0;
37
+ exports.iniciarVerifyMcp = iniciarVerifyMcp;
38
+ exports.consultarVerifyMcp = consultarVerifyMcp;
39
+ exports.executarVerifyMcpWorker = executarVerifyMcpWorker;
40
+ /**
41
+ * Verify assíncrono do MCP: o transporte inicia uma rodada curta e um worker do próprio servidor
42
+ * executa o mesmo `verificar` do CLI. O estado fica no domínio canônico da thread; o modelo nunca
43
+ * escolhe raiz, comando, PID ou lease.
44
+ */
45
+ const fs = __importStar(require("node:fs"));
46
+ const path = __importStar(require("node:path"));
47
+ const node_crypto_1 = require("node:crypto");
48
+ const node_child_process_1 = require("node:child_process");
49
+ const hitl_gates_1 = require("./hitl-gates");
50
+ const manifest_1 = require("./manifest");
51
+ const mcp_artifacts_1 = require("./mcp-artifacts");
52
+ const leases_1 = require("./leases");
53
+ const thread_1 = require("./thread");
54
+ const verify_1 = require("./verify");
55
+ const conducao_1 = require("./conducao");
56
+ const fila_pesada_1 = require("./fila-pesada");
57
+ const mcp_verify_1 = require("./mcp-verify");
58
+ const verify_2 = require("./verify");
59
+ const claims_1 = require("./claims");
60
+ const locale_1 = require("./locale");
61
+ exports.CONTRATO_VERIFY_MCP = 'ork.verify-mcp/v1';
62
+ const ESTADOS_VIVOS = new Set(['na-fila', 'rodando']);
63
+ const UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i;
64
+ const LIMITE_ESTADO = 16 * 1024 * 1024;
65
+ function agora(dep) { return (dep?.agora?.() ?? new Date()).toISOString(); }
66
+ function falhaDaExecucao(e) {
67
+ if (e instanceof fila_pesada_1.ErroDaFilaPesada)
68
+ return { motivo: e.recusa.motivo, detalhe: e.message, recusa: e.recusa };
69
+ if (e instanceof conducao_1.ErroDeConducao)
70
+ return { motivo: e.recusa.motivo, detalhe: e.message, recusa: e.recusa };
71
+ return { motivo: 'verify.worker-failed', detalhe: e.message };
72
+ }
73
+ function inicioDoProcesso(pid) {
74
+ try {
75
+ const stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf8');
76
+ return stat.slice(stat.lastIndexOf(')') + 2).split(' ')[19] ?? null;
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ function processoVivo(processo) {
83
+ try {
84
+ process.kill(processo.pid, 0);
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ return processo.inicio === null || inicioDoProcesso(processo.pid) === processo.inicio;
90
+ }
91
+ function mesmoLease(a, b) {
92
+ return !!a && a.nome === b.nome && a.thread === b.thread && a.pid === b.pid &&
93
+ a.adquiridoEm === b.adquiridoEm && a.expiraEm === b.expiraEm && a.motivo === b.motivo;
94
+ }
95
+ function diretorioDasRodadas(raiz, threadId, criar) {
96
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
97
+ const threadDir = fs.realpathSync((0, thread_1.dirThread)(raiz, threadId));
98
+ const estadoReal = fs.realpathSync(path.join(raiz, '.orkastery'));
99
+ if (!threadDir.startsWith(estadoReal + path.sep))
100
+ throw Error('mcp.verify.async.scope');
101
+ const dir = path.join(threadDir, 'verify-mcp');
102
+ const st = fs.lstatSync(dir, { throwIfNoEntry: false });
103
+ if (!st) {
104
+ if (!criar)
105
+ throw Error('mcp.verify.round.not-found');
106
+ fs.mkdirSync(dir, { mode: 0o700 });
107
+ }
108
+ const atual = fs.lstatSync(dir);
109
+ if (!atual.isDirectory() || atual.isSymbolicLink() || fs.realpathSync(dir) !== dir ||
110
+ (process.getuid && atual.uid !== process.getuid()))
111
+ throw Error('mcp.verify.async.scope');
112
+ return dir;
113
+ }
114
+ function lerArquivoSeguro(file, ausente) {
115
+ let fd;
116
+ try {
117
+ fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
118
+ }
119
+ catch (e) {
120
+ if (ausente && e.code === 'ENOENT')
121
+ return null;
122
+ throw e;
123
+ }
124
+ try {
125
+ const st = fs.fstatSync(fd);
126
+ if (!st.isFile() || st.nlink !== 1 || st.size > LIMITE_ESTADO || (process.getuid && st.uid !== process.getuid()))
127
+ throw Error('mcp.verify.async.unsafe');
128
+ const bytes = fs.readFileSync(fd);
129
+ if (bytes.length > LIMITE_ESTADO)
130
+ throw Error('mcp.verify.async.too-large');
131
+ return bytes;
132
+ }
133
+ finally {
134
+ fs.closeSync(fd);
135
+ }
136
+ }
137
+ function validarEstado(v, threadId, rodadaId) {
138
+ const e = v;
139
+ if (!e || e.contrato !== exports.CONTRATO_VERIFY_MCP || e.threadId !== threadId || e.rodadaId !== rodadaId ||
140
+ !UUID.test(rodadaId) || !['na-fila', 'rodando', 'concluido', 'falhou'].includes(String(e.estado)) ||
141
+ typeof e.criadaEm !== 'string' || typeof e.atualizadaEm !== 'string' ||
142
+ !e.lease || e.lease.thread !== threadId || typeof e.host !== 'string')
143
+ throw Error('mcp.verify.async.invalid');
144
+ return e;
145
+ }
146
+ function lerEstado(dir, threadId, rodadaId) {
147
+ if (!UUID.test(rodadaId))
148
+ throw Error('mcp.verify.round.invalid');
149
+ const bytes = lerArquivoSeguro(path.join(dir, `${rodadaId}.json`), false);
150
+ try {
151
+ return validarEstado(JSON.parse(bytes.toString('utf8')), threadId, rodadaId);
152
+ }
153
+ catch (e) {
154
+ if (e.message.startsWith('mcp.'))
155
+ throw e;
156
+ throw Error('mcp.verify.async.invalid');
157
+ }
158
+ }
159
+ function gravarAtomico(dir, nome, valor) {
160
+ const destino = path.join(dir, nome);
161
+ const temp = path.join(dir, `.${nome}.${process.pid}.${(0, node_crypto_1.randomUUID)()}.tmp`);
162
+ const bytes = JSON.stringify(valor) + '\n';
163
+ if (Buffer.byteLength(bytes) > LIMITE_ESTADO)
164
+ throw Error('mcp.verify.async.too-large');
165
+ try {
166
+ fs.writeFileSync(temp, bytes, { flag: 'wx', mode: 0o600 });
167
+ fs.renameSync(temp, destino);
168
+ }
169
+ finally {
170
+ fs.rmSync(temp, { force: true });
171
+ }
172
+ }
173
+ function gravarEstado(dir, estado) {
174
+ gravarAtomico(dir, `${estado.rodadaId}.json`, estado);
175
+ }
176
+ function lerAtiva(dir, threadId) {
177
+ const bytes = lerArquivoSeguro(path.join(dir, 'active.json'), true);
178
+ if (!bytes)
179
+ return null;
180
+ let id;
181
+ try {
182
+ id = JSON.parse(bytes.toString('utf8')).rodadaId;
183
+ }
184
+ catch {
185
+ throw Error('mcp.verify.async.invalid');
186
+ }
187
+ if (typeof id !== 'string' || !UUID.test(id))
188
+ throw Error('mcp.verify.async.invalid');
189
+ return lerEstado(dir, threadId, id);
190
+ }
191
+ function publico(estado, reutilizada) {
192
+ const { host: _host, processo: _processo, lease: _lease, ...visivel } = estado;
193
+ return { ...visivel, ...(reutilizada === undefined ? {} : { reutilizada }) };
194
+ }
195
+ function vivo(estado, dep) {
196
+ return !!estado.processo && (dep?.processoVivo ?? processoVivo)(estado.processo);
197
+ }
198
+ function liberarLeaseExata(raiz, estado) {
199
+ if (mesmoLease((0, leases_1.lerLease)(raiz, estado.lease.nome), estado.lease))
200
+ (0, leases_1.liberar)(raiz, estado.lease.nome, estado.threadId);
201
+ }
202
+ function falhaDeProcesso(estado, instante) {
203
+ return { ...estado, estado: 'falhou', lane: null, atualizadaEm: instante,
204
+ falha: { motivo: 'verify.processo-morto', detalhe: 'o worker terminou sem gravar um veredito completo' } };
205
+ }
206
+ function prazoDaLease(raiz, threadId) {
207
+ const carregado = (0, manifest_1.exigirManifesto)(raiz);
208
+ const claims = (0, claims_1.lerClaims)(raiz, threadId).filter((c) => c.estado !== 'retirada');
209
+ const quantidade = claims.reduce((n, c) => n + c.verificar.length, 0) + (0, verify_2.comandosDoManifesto)(carregado.manifesto).length + 2;
210
+ const prazos = [carregado.manifesto.verify.timeout_ms ?? 600_000,
211
+ ...Object.values(carregado.manifesto.verify.timeout_ms_por_comando ?? {}).filter((n) => typeof n === 'number')];
212
+ return mcp_verify_1.ESPERA_DA_FILA_PESADA_MCP_MS + Math.max(conducao_1.PRAZO_DE_SESSAO_PADRAO_MS, (0, conducao_1.prazoDaVerificacao)(Math.max(...prazos), quantidade));
213
+ }
214
+ function lancarWorker(pedido) {
215
+ const child = (0, node_child_process_1.spawn)(process.execPath, [pedido.worker, pedido.raiz, pedido.threadId, pedido.rodadaId], {
216
+ cwd: pedido.raiz, detached: true, stdio: 'ignore', env: process.env,
217
+ });
218
+ if (!child.pid)
219
+ throw Error('mcp.verify.worker.spawn');
220
+ child.unref();
221
+ let inicio = null;
222
+ for (let i = 0; i < 20 && inicio === null; i++) {
223
+ inicio = inicioDoProcesso(child.pid);
224
+ if (inicio === null)
225
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5);
226
+ }
227
+ return { pid: child.pid, inicio };
228
+ }
229
+ function iniciarVerifyMcp(raiz, threadId, vinculo, dep = {}) {
230
+ const carregado = (0, manifest_1.exigirManifesto)(raiz);
231
+ if (fs.realpathSync(carregado.raiz) !== fs.realpathSync(raiz))
232
+ throw Error('mcp.verify.project');
233
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
234
+ const thread = (0, thread_1.lerThread)(raiz, threadId);
235
+ if (thread.status === 'fechada' || !thread.worktree)
236
+ throw Error('mcp.verify.thread');
237
+ return (0, hitl_gates_1.comLockHitl)(raiz, threadId, () => {
238
+ const dir = diretorioDasRodadas(raiz, threadId, true);
239
+ const ativa = lerAtiva(dir, threadId);
240
+ if (ativa && ESTADOS_VIVOS.has(ativa.estado) && vivo(ativa, dep))
241
+ return publico(ativa, true);
242
+ if (ativa) {
243
+ const reconciliada = ESTADOS_VIVOS.has(ativa.estado) ? falhaDeProcesso(ativa, agora(dep)) : ativa;
244
+ if (reconciliada !== ativa)
245
+ gravarEstado(dir, reconciliada);
246
+ liberarLeaseExata(raiz, reconciliada);
247
+ }
248
+ const nomeLease = `worktree-write:${threadId}`;
249
+ (0, leases_1.podarRegioesDeThreadsFechadas)(raiz, [nomeLease, 'main-tree'], threadId);
250
+ if ((0, leases_1.lerLease)(raiz, nomeLease) || (0, leases_1.leasesColidentes)(raiz, nomeLease).length || (0, leases_1.leasesColidentes)(raiz, 'main-tree').length)
251
+ throw Error('lease.busy: verify assíncrono exige a worktree livre');
252
+ const rodadaId = (0, node_crypto_1.randomUUID)();
253
+ const instante = agora(dep);
254
+ const regiao = (0, leases_1.adquirirRegiao)(raiz, nomeLease, { thread: threadId, motivo: `MCP VERIFY ASYNC ${rodadaId}`,
255
+ ttlMs: prazoDaLease(raiz, threadId), retomarVencido: false });
256
+ if (!regiao.ok || !regiao.lease)
257
+ throw Error('lease.busy: verify assíncrono não adquiriu a worktree');
258
+ let estado = { contrato: exports.CONTRATO_VERIFY_MCP, rodadaId, threadId, despacho: vinculo.despacho,
259
+ host: vinculo.host, estado: 'na-fila', lane: null, criadaEm: instante, atualizadaEm: instante,
260
+ processo: null, lease: regiao.lease };
261
+ gravarEstado(dir, estado);
262
+ gravarAtomico(dir, 'active.json', { contrato: exports.CONTRATO_VERIFY_MCP, rodadaId });
263
+ try {
264
+ const worker = path.resolve(__dirname, 'mcp-verify-worker.js');
265
+ const processo = (dep.lancar ?? lancarWorker)({ worker, raiz: fs.realpathSync(raiz), threadId, rodadaId });
266
+ estado = { ...estado, processo, atualizadaEm: agora(dep) };
267
+ gravarEstado(dir, estado);
268
+ return publico(estado, false);
269
+ }
270
+ catch (e) {
271
+ estado = { ...estado, estado: 'falhou', atualizadaEm: agora(dep),
272
+ falha: { motivo: 'verify.worker-failed', detalhe: e.message } };
273
+ gravarEstado(dir, estado);
274
+ liberarLeaseExata(raiz, estado);
275
+ return publico(estado, false);
276
+ }
277
+ });
278
+ }
279
+ function consultarVerifyMcp(raiz, threadId, rodadaId, dep = {}) {
280
+ (0, thread_1.lerThread)(raiz, threadId);
281
+ (0, mcp_artifacts_1.validarArquivosEstadoMcp)(raiz, threadId);
282
+ const estado = lerEstado(diretorioDasRodadas(raiz, threadId, false), threadId, rodadaId);
283
+ return publico(ESTADOS_VIVOS.has(estado.estado) && !vivo(estado, dep) ? falhaDeProcesso(estado, agora(dep)) : estado);
284
+ }
285
+ function atualizarRodada(raiz, threadId, rodadaId, mudar) {
286
+ return (0, hitl_gates_1.comLockHitl)(raiz, threadId, () => {
287
+ const dir = diretorioDasRodadas(raiz, threadId, false);
288
+ const atual = lerEstado(dir, threadId, rodadaId);
289
+ const proximo = mudar(atual);
290
+ gravarEstado(dir, proximo);
291
+ return proximo;
292
+ });
293
+ }
294
+ /** Entrada do processo destacado; exportada para o teste hermético executar a mesma rodada sem transporte. */
295
+ function executarVerifyMcpWorker(raiz, threadId, rodadaId) {
296
+ const dir = diretorioDasRodadas(raiz, threadId, false);
297
+ let inicial = lerEstado(dir, threadId, rodadaId);
298
+ const inicioAtual = inicioDoProcesso(process.pid);
299
+ if (!inicial.processo || inicial.processo.pid !== process.pid ||
300
+ (inicial.processo.inicio !== null && inicial.processo.inicio !== inicioAtual))
301
+ throw Error('mcp.verify.worker.identity');
302
+ if (!mesmoLease((0, leases_1.lerLease)(raiz, inicial.lease.nome), inicial.lease))
303
+ throw Error('mcp.verify.worker.lease');
304
+ const atualizar = (mudanca) => {
305
+ inicial = atualizarRodada(raiz, threadId, rodadaId, (atual) => {
306
+ if (!mesmoLease((0, leases_1.lerLease)(raiz, atual.lease.nome), atual.lease))
307
+ return { ...atual, estado: 'falhou', lane: null, atualizadaEm: new Date().toISOString(),
308
+ falha: { motivo: 'verify.lease-perdida', detalhe: 'a lease da worktree mudou durante a rodada' } };
309
+ return { ...atual, ...mudanca, atualizadaEm: new Date().toISOString() };
310
+ });
311
+ if (inicial.estado === 'falhou')
312
+ throw Error(inicial.falha?.motivo ?? 'verify.worker-failed');
313
+ };
314
+ // RM-064: o estado terminal só é gravado depois de a lease voltar. Quem lê `concluido` ou `falhou`
315
+ // pode contar com a worktree livre; a ordem inversa deixava a recusa visível com a lease ainda presa.
316
+ let terminal = null;
317
+ try {
318
+ atualizar({ estado: 'na-fila', lane: 'fila-pesada' });
319
+ const carregado = (0, manifest_1.exigirManifesto)(raiz);
320
+ const resultado = (0, locale_1.comLocale)('pt-BR', () => (0, verify_1.verificar)(carregado, threadId, {
321
+ operacao: 'mcp.verify',
322
+ conducao: { canal: inicial.host, identidade: inicial.despacho,
323
+ filaPesada: { esperarMs: mcp_verify_1.ESPERA_DA_FILA_PESADA_MCP_MS, aoEsperar: (ocupantes) => atualizar({ estado: 'na-fila',
324
+ lane: 'fila-pesada', ocupantes: ocupantes.map((o) => ({ operacao: o.operacao, thread: o.thread })) }) } },
325
+ progresso: (lane) => atualizar({ estado: 'rodando', lane, ocupantes: undefined }),
326
+ }));
327
+ const locale = (0, locale_1.resolverLocale)(carregado.manifesto.owner?.language).locale;
328
+ const veredito = { ok: resultado.ok, commit: resultado.commit, motivos: resultado.motivos,
329
+ check: resultado.check, texto: (0, locale_1.comLocale)(locale, () => (0, verify_1.textoDoVerify)(resultado)), resultado };
330
+ terminal = { estado: 'concluido', lane: null, veredito, falha: undefined, ocupantes: undefined };
331
+ }
332
+ catch (e) {
333
+ terminal = { estado: 'falhou', lane: null, falha: falhaDaExecucao(e), ocupantes: undefined };
334
+ }
335
+ finally {
336
+ const atual = lerEstado(dir, threadId, rodadaId);
337
+ const leaseNossa = mesmoLease((0, leases_1.lerLease)(raiz, atual.lease.nome), atual.lease);
338
+ liberarLeaseExata(raiz, atual);
339
+ if (atual.estado === 'falhou')
340
+ terminal = null; // a falha já gravada (lease perdida, por exemplo) prevalece
341
+ else if (!leaseNossa)
342
+ terminal = { estado: 'falhou', lane: null, ocupantes: undefined,
343
+ falha: { motivo: 'verify.lease-perdida', detalhe: 'a lease da worktree mudou durante a rodada' } };
344
+ if (terminal) {
345
+ const fim = terminal;
346
+ try {
347
+ inicial = atualizarRodada(raiz, threadId, rodadaId, (a) => ({ ...a, ...fim, atualizadaEm: new Date().toISOString() }));
348
+ }
349
+ catch { /* a leitura abaixo projeta morte/falha; nunca inventa verde */ }
350
+ }
351
+ }
352
+ return consultarVerifyMcp(raiz, threadId, rodadaId);
353
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /** Processo destacado do verify MCP. Toda autoridade vem do estado criado pelo servidor. */
4
+ const mcp_verify_async_1 = require("./mcp-verify-async");
5
+ if (require.main === module) {
6
+ const [raiz, threadId, rodadaId, ...resto] = process.argv.slice(2);
7
+ if (!raiz || !threadId || !rodadaId || resto.length)
8
+ process.exitCode = 2;
9
+ else {
10
+ try {
11
+ (0, mcp_verify_async_1.executarVerifyMcpWorker)(raiz, threadId, rodadaId);
12
+ }
13
+ catch {
14
+ process.exitCode = 1;
15
+ }
16
+ }
17
+ }
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ESPERA_DA_FILA_PESADA_MCP_MS = void 0;
37
+ exports.diagnosticoDoVerifyMcp = diagnosticoDoVerifyMcp;
37
38
  exports.verificarMcp = verificarMcp;
38
39
  /** Operação estruturada: argumentos só identificam a thread; execução sempre confinada. */
39
40
  const fs = __importStar(require("node:fs"));
@@ -68,6 +69,14 @@ function mesmoLease(a, b) {
68
69
  return !!a && a.nome === b.nome && a.thread === b.thread && a.pid === b.pid &&
69
70
  a.adquiridoEm === b.adquiridoEm && a.expiraEm === b.expiraEm && a.motivo === b.motivo;
70
71
  }
72
+ /** Parte pura do retorno MCP: permite provar o contrato mesmo quando o sandbox nao abre outro sandbox. */
73
+ function diagnosticoDoVerifyMcp(r) {
74
+ return {
75
+ regressoes: r.regressoes.map(c => c.nome),
76
+ preExistentes: r.preExistentes.map(c => c.nome),
77
+ comparacoes: r.comparacoes,
78
+ };
79
+ }
71
80
  /** Síncrono: prazo total/per-command, não cancelamento reativo do event loop. */
72
81
  function verificarMcp(raizEntrada, threadId, conducao = {}) {
73
82
  const deadline = Date.now() + PRAZO_TOTAL_MS;
@@ -167,7 +176,7 @@ function verificarMcp(raizEntrada, threadId, conducao = {}) {
167
176
  execucoes: execucoes.slice(0, LIMITE_RETORNO).map(c => ({ nome: c.nome, ok: c.ok, code: c.code,
168
177
  executado: c.executado === true, resumo: c.resumo })),
169
178
  totalExecucoes: execucoes.length, omitidas: Math.max(0, execucoes.length - LIMITE_RETORNO),
170
- regressoes: r.regressoes.map(c => c.nome), preExistentes: r.preExistentes.map(c => c.nome),
179
+ ...diagnosticoDoVerifyMcp(r),
171
180
  executor: 'codex-sandbox', prazoTotalMs: PRAZO_TOTAL_MS, limitePorComandoMs: COMANDO_MAX_MS,
172
181
  };
173
182
  }
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MOTIVOS_DE_PEDIDO_A_CONDUCAO = void 0;
4
+ exports.registrarPedidoAConducao = registrarPedidoAConducao;
5
+ /**
6
+ * Pedido estreito da sessao de fase para a conducao. A sessao nao executa o comando,
7
+ * nao aprova gate e nao assume o dono: ela apenas registra por que parou e o passo exato.
8
+ */
9
+ const gates_1 = require("./gates");
10
+ const ledger_1 = require("./ledger");
11
+ const thread_1 = require("./thread");
12
+ exports.MOTIVOS_DE_PEDIDO_A_CONDUCAO = [
13
+ 'concurrency.lease-indeterminado',
14
+ 'conducao.acao-necessaria',
15
+ ];
16
+ function comandoSeguro(comando) {
17
+ return comando.length <= 1000 && /^ork [^\s]/u.test(comando) && !/[\p{Cc}\p{Cf};|&<>`$\\()]/u.test(comando) &&
18
+ !/\s{2,}/.test(comando) && comando.trim() === comando;
19
+ }
20
+ /** Registra uma vez, sempre correlacionado ao despacho que originou o servidor MCP filho. */
21
+ function registrarPedidoAConducao(raiz, threadId, entrada) {
22
+ if (!exports.MOTIVOS_DE_PEDIDO_A_CONDUCAO.includes(entrada.motivo))
23
+ throw Error('conducao.request.reason.invalid');
24
+ if (!comandoSeguro(entrada.comando)) {
25
+ throw Error('conducao.request.command.invalid: informe um comando ork de uma linha, sem operadores de shell');
26
+ }
27
+ const thread = (0, thread_1.lerThread)(raiz, threadId), dir = (0, thread_1.dirThread)(raiz, threadId), eventos = (0, ledger_1.lerLedger)(dir);
28
+ const despacho = eventos.filter(e => e.tipo === 'phase_dispatch' &&
29
+ e.identidade?.dispatchId === entrada.despacho).at(-1);
30
+ if (!despacho || despacho.thread !== threadId || typeof despacho.sessionId !== 'string' || typeof despacho.fase !== 'string') {
31
+ throw Error('conducao.request.dispatch.invalid: o servidor nao corresponde a um despacho provado desta thread');
32
+ }
33
+ const sessao = thread.sessoes.find(s => s.sessionId === despacho.sessionId && s.fase === despacho.fase);
34
+ if (!sessao)
35
+ throw Error('conducao.request.session.invalid: o despacho nao esta vinculado a sessao ativa da thread');
36
+ const existente = eventos.filter(e => e.tipo === 'gate_blocked' && e.origem === 'mcp.request_conduction' &&
37
+ e.despacho === entrada.despacho && e.motivo === entrada.motivo && e.comando === entrada.comando).at(-1);
38
+ if (existente)
39
+ return { ok: true, criado: false, eventId: existente.eventId, motivo: entrada.motivo,
40
+ comando: entrada.comando, fase: despacho.fase, sessionId: despacho.sessionId, despacho: entrada.despacho };
41
+ const evento = (0, gates_1.registrarGateBloqueado)(dir, threadId, {
42
+ gate: 'phase.dispatch', motivo: entrada.motivo, modo: thread.modo,
43
+ detalhe: entrada.detalhe, correcao: entrada.comando,
44
+ evidencia: `despacho ${entrada.despacho} da sessao ${despacho.sessionId}`,
45
+ fase: despacho.fase, sessionId: despacho.sessionId, despachoEm: sessao.despachadaEm,
46
+ despacho: entrada.despacho, comando: entrada.comando, origem: 'mcp.request_conduction',
47
+ pedidoConducao: { contrato: 'ork.pedido-conducao/v1', host: entrada.host, estado: 'aberto' },
48
+ });
49
+ return { ok: true, criado: true, eventId: evento.eventId, motivo: entrada.motivo,
50
+ comando: entrada.comando, fase: despacho.fase, sessionId: despacho.sessionId, despacho: entrada.despacho };
51
+ }
package/dist/preflight.js CHANGED
@@ -126,49 +126,46 @@ function sondasDeAmbiente(raiz, umask = umaskDoProcesso()) {
126
126
  * com login conferido pelo proprio CLI. A sonda nao marca o store: quem marca e o despacho.
127
127
  */
128
128
  function checkDeContas(raiz, runtime, auth = authPadrao) {
129
+ const m = (0, locale_1.msg)().preflight;
129
130
  let perfis;
130
131
  try {
131
132
  perfis = (0, runtime_profiles_1.perfisDoRuntime)((0, runtime_profiles_1.lerPerfisComContas)(raiz), runtime).filter(p => p.estado !== 'desativado');
132
133
  }
133
134
  catch (e) {
134
- return { nome: 'contas', nivel: 'fail', detalhe: `store de perfis invalido: ${e.message}` };
135
+ return { nome: 'contas', nivel: 'fail', detalhe: m.storeInvalido(e.message) };
135
136
  }
136
137
  if (!perfis.length)
137
- return { nome: 'contas', nivel: 'ok', detalhe: `${runtime} sem perfil configurado: despacho pelo ambiente do processo` };
138
+ return { nome: 'contas', nivel: 'ok', detalhe: m.semPerfil(runtime) };
138
139
  const estados = perfis.map(p => {
139
140
  const a = auth((0, runtime_profiles_1.perfilDeDespacho)(p));
140
141
  return { id: p.id, pronto: a.ok && (0, runtime_profiles_1.perfilDisponivel)(p),
141
- texto: !a.ok ? `${a.pago ? 'provider pago, nunca despacha' : a.transitorio ? 'conferencia de login inconclusiva' : 'sem login'} (${a.detalhe})` : (0, runtime_profiles_1.perfilDisponivel)(p) ? 'pronto' : `${p.estado}${p.esgotadoAte ? ` ate ${p.esgotadoAte}` : ''}` };
142
+ texto: !a.ok ? `${a.pago ? m.estadoConta.providerPago : a.transitorio ? m.estadoConta.loginInconclusivo : m.estadoConta.semLogin} (${a.detalhe})`
143
+ : (0, runtime_profiles_1.perfilDisponivel)(p) ? m.estadoConta.pronto : m.estadoConta.esgotadoAte(p.estado, p.esgotadoAte ?? '') };
142
144
  });
143
145
  const prontos = estados.filter(e => e.pronto).length;
144
146
  return { nome: 'contas', nivel: prontos === 0 ? 'fail' : prontos < estados.length ? 'warn' : 'ok',
145
- detalhe: `${runtime}: ${estados.map(e => `${e.id} ${e.texto}`).join('; ')}`,
146
- ...(prontos < estados.length ? { correcao: 'ork accounts check marca os perfis; ork accounts add refaz o login pelo proprio CLI' } : {}) };
147
+ detalhe: m.contas(runtime, estados.map(e => `${e.id} ${e.texto}`).join('; ')),
148
+ ...(prontos < estados.length ? { correcao: m.contasCorrecao } : {}) };
147
149
  }
148
- const limites = [
149
- 'Sonda contextual local: não inventaria sessões, não inicia modelo e não autoriza despacho.',
150
- 'Aceitação do modelo, instalação do adaptador/MCP, contexto da thread, gates e policies do pedido são revalidados no despacho; com perfis, o login de cada conta é conferido aqui e de novo no despacho.',
151
- 'Trio sem override; um override posterior exige resolver e verificar novamente o despacho.',
152
- 'VERIFY/SHIP MCP exigem executor Codex isolado próprio e transporte SHIP válido; a sonda de despacho não comprova entrega.',
153
- ];
154
150
  function preflight(raiz, modo, nomesHerdados = (0, runtime_ambiente_1.nomesDeProviderAtivos)(), sensores = sensoresPadrao) {
151
+ const t = (0, locale_1.msg)().preflight;
155
152
  // I-43: a matriz tem os cinco (para LER thread antiga); quem entra aqui e ESCRITA.
156
153
  if (!modos_1.ORDEM_DOS_MODOS.includes(modo))
157
154
  throw Error('preflight.mode.invalid');
158
155
  const checks = [];
159
- checks.push({ nome: 'node', nivel: Number(process.versions.node.split('.')[0]) >= 20 ? 'ok' : 'fail', detalhe: 'Node 20 ou superior necessário' });
156
+ checks.push({ nome: 'node', nivel: Number(process.versions.node.split('.')[0]) >= 20 ? 'ok' : 'fail', detalhe: t.node });
160
157
  const git = !!(0, util_1.noPath)('git');
161
- checks.push({ nome: 'git', nivel: git ? 'ok' : 'fail', detalhe: git ? 'Git disponível' : 'Git ausente' });
158
+ checks.push({ nome: 'git', nivel: git ? 'ok' : 'fail', detalhe: git ? t.gitOk : t.gitAusente });
162
159
  const repo = git ? (0, util_1.exec)('git', ['rev-parse', '--is-inside-work-tree'], raiz, 10000) : null;
163
- checks.push({ nome: 'repositorio', nivel: repo?.ok && repo.stdout.trim() === 'true' ? 'ok' : 'fail', detalhe: 'Repositório Git local necessário' });
160
+ checks.push({ nome: 'repositorio', nivel: repo?.ok && repo.stdout.trim() === 'true' ? 'ok' : 'fail', detalhe: t.repo });
164
161
  checks.push(...sondasDeAmbiente(raiz));
165
162
  const carregado = (0, manifest_1.carregarManifesto)(raiz);
166
163
  const blocos = [];
167
- checks.push({ nome: 'manifesto', nivel: !carregado || carregado.erros.length ? 'fail' : 'ok', detalhe: !carregado ? 'Manifesto ausente' : carregado.erros.length ? 'Manifesto inválido; corrija antes do despacho' : 'Manifesto válido' });
164
+ checks.push({ nome: 'manifesto', nivel: !carregado || carregado.erros.length ? 'fail' : 'ok', detalhe: !carregado ? t.manifestoAusente : carregado.erros.length ? t.manifestoInvalido : t.manifestoValido });
168
165
  if (carregado && !carregado.erros.length) {
169
166
  const m = carregado.manifesto;
170
- checks.push({ nome: 'modo', nivel: m.conduction.allowed_modes.includes(modo) ? 'ok' : 'fail', detalhe: `Modo solicitado: ${modo}` });
171
- checks.push({ nome: 'abbrev', nivel: (0, slug_1.validarAbbrev)(m.project.abbrev).ok ? 'ok' : 'fail', detalhe: 'Identidade curta do projeto validada' });
167
+ checks.push({ nome: 'modo', nivel: m.conduction.allowed_modes.includes(modo) ? 'ok' : 'fail', detalhe: t.modo(modo) });
168
+ checks.push({ nome: 'abbrev', nivel: (0, slug_1.validarAbbrev)(m.project.abbrev).ok ? 'ok' : 'fail', detalhe: t.abbrev });
172
169
  const setup = (0, setup_1.lerSetup)(carregado.raiz), observados = new Map(), contas = new Map();
173
170
  let sandbox;
174
171
  for (const [i, bloco] of modos_1.MODOS[modo].blocos.entries()) {
@@ -177,11 +174,11 @@ function preflight(raiz, modo, nomesHerdados = (0, runtime_ambiente_1.nomesDePro
177
174
  try {
178
175
  trio = (0, phase_1.resolverDespacho)(m, {}, (0, setup_1.configDoBloco)(setup, modo, bloco.fases[0]));
179
176
  (0, runtimes_1.resolverRuntime)(trio.runtime);
180
- locais.push({ nome: 'trio', nivel: 'ok', detalhe: 'Runtime/modelo/esforço resolvidos pelo mesmo contrato do despacho; aceitação nativa não comprovada' });
177
+ locais.push({ nome: 'trio', nivel: 'ok', detalhe: t.trio });
181
178
  if (!observados.has(trio.runtime))
182
179
  observados.set(trio.runtime, sensores.runtime(trio.runtime));
183
180
  const presente = observados.get(trio.runtime);
184
- locais.push({ nome: 'runtime', nivel: presente ? 'ok' : 'fail', detalhe: presente ? `${trio.runtime} disponível` : `${trio.runtime} ausente` });
181
+ locais.push({ nome: 'runtime', nivel: presente ? 'ok' : 'fail', detalhe: presente ? t.runtimeOk(trio.runtime) : t.runtimeAusente(trio.runtime) });
185
182
  if (presente) {
186
183
  if (!contas.has(trio.runtime))
187
184
  contas.set(trio.runtime, checkDeContas(carregado.raiz, trio.runtime, sensores.auth ?? authPadrao));
@@ -189,14 +186,15 @@ function preflight(raiz, modo, nomesHerdados = (0, runtime_ambiente_1.nomesDePro
189
186
  }
190
187
  if (trio.runtime === 'codex' && presente) {
191
188
  sandbox ??= sensores.sandboxCodex();
192
- locais.push({ nome: 'sandbox de despacho', nivel: sandbox ? 'ok' : m.runtime.sandbox === 'danger-full-access' ? 'warn' : 'fail', detalhe: sandbox ? 'Sonda Codex sem modelo executou' : 'Sonda Codex falhou; não altere o sandbox para contornar a falha' });
189
+ locais.push({ nome: 'sandbox de despacho', nivel: sandbox ? 'ok' : m.runtime.sandbox === 'danger-full-access' ? 'warn' : 'fail', detalhe: sandbox ? t.sandboxOk : t.sandboxFalhou });
193
190
  }
194
191
  const ativos = nomesHerdados.filter(n => runtime_ambiente_1.ENVS_DE_PROVIDER_PAGO.includes(n));
195
192
  const relevantes = ativos.filter(n => trio.runtime === 'claude-bg' ? /^(ANTHROPIC_|CLAUDE_CODE_USE_)/.test(n) : /^(OPENAI_|OPENROUTER_)/.test(n));
196
- locais.push({ nome: 'provider', nivel: m.runtime.provider_policy === 'subscription-only' && relevantes.length ? 'fail' : ativos.length ? 'warn' : 'ok', detalhe: relevantes.length ? `Nomes de provider incompatíveis com assinatura: ${relevantes.join(', ')}` : ativos.length ? 'Há nomes de provider alheios ao runtime; não usar credenciais pagas' : 'Nenhum nome de provider pago ativo' });
193
+ locais.push({ nome: 'provider', nivel: m.runtime.provider_policy === 'subscription-only' && relevantes.length ? 'fail' : ativos.length ? 'warn' : 'ok',
194
+ detalhe: relevantes.length ? t.providerIncompativel(relevantes.join(', ')) : ativos.length ? t.providerAlheio : t.providerNenhum });
197
195
  }
198
196
  catch {
199
- locais.push({ nome: 'trio', nivel: 'fail', detalhe: 'Setup/trio ou sonda inválidos; complete o modelo e confira o runtime no setup canônico' });
197
+ locais.push({ nome: 'trio', nivel: 'fail', detalhe: t.trioInvalido });
200
198
  }
201
199
  blocos.push({ numero: i + 1, fases: [...bloco.fases], trio, checks: locais, pronto: locais.every(c => c.nivel !== 'fail') });
202
200
  }
@@ -205,16 +203,17 @@ function preflight(raiz, modo, nomesHerdados = (0, runtime_ambiente_1.nomesDePro
205
203
  return { modo, scope: 'dispatch-local', checks, blocos,
206
204
  prontoPrimeiroBloco: basePronta && !!blocos[0]?.pronto,
207
205
  prontoTodosBlocos: basePronta && blocos.length > 0 && blocos.every(b => b.pronto),
208
- entrega: { verificada: false, dependencias: ['executor Codex isolado', 'transporte SHIP instalado', 'gates e HEADs correntes'] }, limites: [...limites] };
206
+ entrega: { verificada: false, dependencias: ['executor Codex isolado', 'transporte SHIP instalado', 'gates e HEADs correntes'] }, limites: [...t.limites] };
209
207
  }
210
208
  function textoPreflight(r) {
211
209
  // I-35: o Check guarda o ISO (dado de maquina); quem imprime para o dono localiza.
212
210
  let localizou = false;
213
211
  const local = (t) => { const l = (0, horario_1.localizarTexto)(t); if (l !== t)
214
212
  localizou = true; return l; };
215
- const linhas = ['ork doctor contextual: ' + r.modo, ...r.checks.map(c => `${c.nivel}: ${c.nome}: ${local(c.detalhe)}`),
216
- ...r.blocos.flatMap(b => [`bloco ${b.numero} ${b.fases.join('→')}: ${b.trio ? `${b.trio.runtime}/${b.trio.model}/${b.trio.effort}` : 'trio pendente'}`, ...b.checks.map(c => ` ${c.nivel}: ${c.nome}: ${local(c.detalhe)}`)]),
217
- `Primeiro bloco: ${r.prontoPrimeiroBloco ? 'sonda pronta' : 'bloqueado'}; todos os blocos: ${r.prontoTodosBlocos ? 'sonda pronta' : 'dependências pendentes'}`, ...r.limites];
213
+ const m = (0, locale_1.msg)().preflight, nome = (v) => m.nomes[v] ?? v;
214
+ const linhas = [m.titulo(r.modo), ...r.checks.map(c => m.check(c.nivel, nome(c.nome), local(c.detalhe))),
215
+ ...r.blocos.flatMap(b => [m.bloco(b.numero, b.fases.join('→'), b.trio ? `${b.trio.runtime}/${b.trio.model}/${b.trio.effort}` : null),
216
+ ...b.checks.map(c => m.checkDoBloco(c.nivel, nome(c.nome), local(c.detalhe)))]), m.resumo(r.prontoPrimeiroBloco, r.prontoTodosBlocos), ...r.limites];
218
217
  // O fuso sai uma vez por mensagem, depois dos limites.
219
218
  if (localizou)
220
219
  linhas.push((0, horario_1.legendaDoFuso)());
package/dist/prompts.js CHANGED
@@ -292,7 +292,7 @@ function lintTemplate(t, contrato) {
292
292
  */
293
293
  exports.TEMPLATE_FASE_PADRAO = `---
294
294
  id: fase-padrao
295
- versao: 4
295
+ versao: 5
296
296
  descricao: Prompt canonico de uma fase do ciclo GOAL..MASTER, com nomenclatura, modo de conducao, contexto da thread, memoria injetada e regras de evidencia.
297
297
  fases: [GOAL, PLAN, GO, CHECK, SHIP, MASTER]
298
298
  variaveis: [fase, thread, bloco, nome, ciclo_canonico, tag, blocos, pausas, linha_variante, regra_de_pausa, invariantes, slug, projeto_nome, projeto_abbrev, base_branch, base_commit, diretorio, trabalho, pedido, fecho_go, memoria_injetada, regras_de_evidencia]
@@ -307,6 +307,7 @@ Quando disponiveis, use ork_artifact_read/write para objetivo, plano e parecer,
307
307
  Decisao que voce tomar sem perguntar ao dono vai ao ledger por \`ork decisao registrar <thread>\` (no MCP, \`ork_decision_record\`, pois o sandbox do codex nao grava o ledger), com o que foi decidido, o porque, como mudar, o custo de reverter agora e depois, o criterio escrito, quem decidiu e a evidencia; e assim que ela chega ao dono no resumo, em vez de sumir na conversa.
308
308
  Artefato de autoria do agente nao e recibo oficial; cadastrar claim nao significa verifica-la.
309
309
  Para registrar produto, prefira ork_git_commit com HEAD atual e paths com claims; use ork_verify para verificacao real e ork_ship com HEADs fonte/destino conferidos para entrega. Commit local nao e SHIP; retorno incomplete exige reconciliar estado e remoto antes de repetir. Permissao para consultar nao concede essas operacoes: respeite a autorizacao nativa e os gates.
310
+ Quando o MCP expuser \`ork_verify_status\`, chame \`ork_verify\` uma vez, guarde o \`rodadaId\` e consulte a mesma rodada a cada 30–60 s. Enquanto ela estiver na fila ou rodando, retenha apenas estado e lane atuais no contexto; nao repita o pedido nem copie payloads intermediarios. So avance com o veredito terminal verde; falha tipada ou veredito vermelho bloqueia o ciclo.
310
311
  Decisao do dono deve ser apresentada pela sessao condutora ao host; a sessao filha nao responde nem assume a identidade do dono.
311
312
  Preserve o perfil filho configurado: \`interactive\` concede consultas; \`worktree\` exige opt-in da instalacao e autoriza ferramentas exatas na WT validada, sem aprovar gates ou assumir o dono. Nao mude configuracao para contornar bloqueio.
312
313
  Consulte \`ork_git_status\` para HEADs reais e estado Git; use \`ork_git_commit\`, \`ork_verify\` e \`ork_ship\` pelos contratos do nucleo, sem Bash Git nem inventar SHAs. Use \`source.head\` como \`expectedHead\` do commit; consulte novamente apos commit e use \`source.head\`/\`destination.head\` como \`expectedSource\`/\`expectedDestination\` do SHIP. Base historica nao substitui a consulta atual. As regras Edit/deny sao permissoes nativas de ferramentas, nao sandbox de processos.
@@ -351,7 +352,7 @@ Falha de autenticação permanece impedimento; não troque provider, sandbox ou
351
352
  /** English counterpart of the canonical phase prompt. The pt-BR template above remains the source contract. */
352
353
  exports.TEMPLATE_FASE_PADRAO_EN = `---
353
354
  id: fase-padrao.en
354
- versao: 4
355
+ versao: 5
355
356
  descricao: Canonical prompt for a GOAL..MASTER cycle phase, with naming, conduction mode, thread context, injected memory, and evidence rules.
356
357
  fases: [GOAL, PLAN, GO, CHECK, SHIP, MASTER]
357
358
  variaveis: [fase, thread, bloco, nome, ciclo_canonico, tag, blocos, pausas, linha_variante, regra_de_pausa, invariantes, slug, projeto_nome, projeto_abbrev, base_branch, base_commit, diretorio, trabalho, pedido, fecho_go, memoria_injetada, regras_de_evidencia]
@@ -366,6 +367,7 @@ When available, use ork_artifact_read/write for the goal, plan, and review, and
366
367
  Record every decision you make without asking the owner in the ledger with \`ork decisao registrar <thread>\` (or \`ork_decision_record\` in MCP because the Codex sandbox cannot write the ledger), including what was decided, why, how to change it, reversal cost now and later, the written criterion, who decided, and the evidence. This is how it reaches the owner in the summary instead of disappearing in the conversation.
367
368
  An agent-authored artifact is not an official receipt; registering a claim does not verify it.
368
369
  To record product changes, prefer ork_git_commit with the current HEAD and paths with claims; use ork_verify for real verification and ork_ship with checked source/destination HEADs for delivery. A local commit is not SHIP; an incomplete result requires reconciling state and remote before retrying. Permission to query does not grant those operations: respect native authorization and gates.
370
+ When MCP exposes \`ork_verify_status\`, call \`ork_verify\` once, keep its \`rodadaId\`, and query that same round every 30–60 seconds. While queued or running, retain only the current state and lane in context; do not repeat the request or copy intermediate payloads. Continue only after a terminal green verdict; a typed failure or red verdict blocks the cycle.
369
371
  The conducting session presents an owner decision to the host; a child session neither answers nor assumes the owner's identity.
370
372
  Preserve the configured child profile: \`interactive\` grants queries; \`worktree\` requires installation opt-in and authorizes exact tools in the validated worktree, without approving gates or assuming the owner. Do not change configuration to bypass a block.
371
373
  Query \`ork_git_status\` for real HEADs and Git state; use \`ork_git_commit\`, \`ork_verify\`, and \`ork_ship\` through the core contracts, without Bash Git or invented SHAs. Use \`source.head\` as the commit \`expectedHead\`; query again after the commit and use \`source.head\`/\`destination.head\` as SHIP \`expectedSource\`/\`expectedDestination\`. A historical base does not replace the current query. Edit/deny rules are native tool permissions, not process sandbox rules.
package/dist/pulse.js CHANGED
@@ -157,7 +157,8 @@ function comporPulse(carregado, entrada) {
157
157
  if (p.motivo === 'human.pending' && p.fonte === 'ledger' && entregas?.doCondutor.gates.has(`${l.thread}|${p.fase}`))
158
158
  continue;
159
159
  if (ehRegistro(l.thread)) {
160
- if (p.fonte === 'escalonador' || ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'vaga.stale', 'runtime.silencio'].includes(p.motivo))
160
+ if (p.fonte === 'escalonador' || ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'vaga.stale',
161
+ 'runtime.silencio', 'concurrency.lease-indeterminado', 'conducao.acao-necessaria'].includes(p.motivo))
161
162
  continue;
162
163
  const sessoes = radar.sessoes.filter(s => s.thread?.id === l.thread && s.thread.fase === p.fase);
163
164
  // Escalada explícita de ledger não é apagada pela conclusão de uma sessão.
@@ -213,7 +214,8 @@ function comporPulse(carregado, entrada) {
213
214
  }
214
215
  }
215
216
  // I-45: vaga parada e sinal do escalonador (`ork board reap` devolve a vaga), nao pergunta ao dono.
216
- const esperaAutomatica = ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'runtime.rate-limited', 'vaga.stale'].includes(p.motivo);
217
+ const esperaAutomatica = ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'runtime.rate-limited', 'vaga.stale',
218
+ 'concurrency.lease-indeterminado', 'conducao.acao-necessaria'].includes(p.motivo);
217
219
  (esperaAutomatica ? automaticas : humanos).push(item);
218
220
  }
219
221
  // RM-063 (F5): o MASTER por omissao da varredura nao vira item do dono (I-45: a entrega sem nota fica na faixa