sinapse-ai 1.19.1 → 1.19.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.
- package/.sinapse-ai/core/execution/build-orchestrator.js +31 -0
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +15 -2
- package/.sinapse-ai/core/orchestration/gate-evaluator.js +78 -3
- package/.sinapse-ai/install-manifest.yaml +7 -7
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
|
@@ -231,6 +231,11 @@ class BuildOrchestrator extends EventEmitter {
|
|
|
231
231
|
// quality gate can inspect its honesty (degraded/stub/subtasks). Without this
|
|
232
232
|
// the epic4_to_epic6 gate has nothing real to bite on.
|
|
233
233
|
plan: ctx.plan,
|
|
234
|
+
// epic: empty-build-honesty — surface the REAL files the build wrote/modified
|
|
235
|
+
// (collected from the build loop's per-subtask results). Without this the
|
|
236
|
+
// downstream `implementation_exists` gate cannot tell a real build apart from
|
|
237
|
+
// an empty "success" that touched zero code files.
|
|
238
|
+
filesModified: this._collectModifiedFiles(ctx),
|
|
234
239
|
};
|
|
235
240
|
} catch (error) {
|
|
236
241
|
ctx.errors.push(error);
|
|
@@ -263,6 +268,32 @@ class BuildOrchestrator extends EventEmitter {
|
|
|
263
268
|
}
|
|
264
269
|
}
|
|
265
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Collect the real files the build wrote/modified from the build-loop result.
|
|
273
|
+
*
|
|
274
|
+
* epic: empty-build-honesty — the per-subtask results carry `filesModified`
|
|
275
|
+
* (extracted from the agent output). This flattens + de-duplicates them into a
|
|
276
|
+
* single honest list. Returns [] when nothing was touched (an empty build), which
|
|
277
|
+
* is precisely the signal the `implementation_exists` gate needs to block.
|
|
278
|
+
*
|
|
279
|
+
* @param {Object} ctx - Build context ({ result: { results: [...] } })
|
|
280
|
+
* @returns {string[]} De-duplicated list of modified files
|
|
281
|
+
* @private
|
|
282
|
+
*/
|
|
283
|
+
_collectModifiedFiles(ctx) {
|
|
284
|
+
const results = ctx && ctx.result && Array.isArray(ctx.result.results) ? ctx.result.results : [];
|
|
285
|
+
const files = [];
|
|
286
|
+
for (const r of results) {
|
|
287
|
+
const list = r && Array.isArray(r.filesModified) ? r.filesModified : [];
|
|
288
|
+
for (const f of list) {
|
|
289
|
+
if (typeof f === 'string' && f.trim() !== '' && !files.includes(f)) {
|
|
290
|
+
files.push(f);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return files;
|
|
295
|
+
}
|
|
296
|
+
|
|
266
297
|
/**
|
|
267
298
|
* Run a phase with event emission and error handling
|
|
268
299
|
*/
|
|
@@ -113,13 +113,23 @@ class Epic4Executor extends EpicExecutor {
|
|
|
113
113
|
if (this._realExecutionAllowed()) {
|
|
114
114
|
const buildResult = await this._executeViaBuildOrchestrator(storyId, context);
|
|
115
115
|
if (buildResult && buildResult.success) {
|
|
116
|
+
// Honesty invariant (epic: empty-build-honesty): do NOT set
|
|
117
|
+
// `implementationPath = planPath`. A plan path ALWAYS exists and is NOT
|
|
118
|
+
// proof of implementation — the multi-story measurement caught the gate
|
|
119
|
+
// approving an empty build because of exactly this. Surface the REAL files
|
|
120
|
+
// the build touched so the epic4_to_epic6 gate can tell an implementation
|
|
121
|
+
// apart from an empty "success".
|
|
122
|
+
const codeChanges = Array.isArray(buildResult.filesModified)
|
|
123
|
+
? buildResult.filesModified
|
|
124
|
+
: [];
|
|
116
125
|
return this._completeExecution({
|
|
117
|
-
implementationPath: planPath,
|
|
118
126
|
planPath,
|
|
119
127
|
// F5 (epic: orchestration-consolidation): propagate the real plan object
|
|
120
128
|
// so the downstream epic4_to_epic6 gate can verify it is not degraded/stub.
|
|
121
129
|
plan: buildResult.plan,
|
|
122
130
|
build: buildResult,
|
|
131
|
+
codeChanges,
|
|
132
|
+
filesModified: codeChanges,
|
|
123
133
|
reportPath: buildResult.reportPath,
|
|
124
134
|
phases: buildResult.phases,
|
|
125
135
|
});
|
|
@@ -144,7 +154,10 @@ class Epic4Executor extends EpicExecutor {
|
|
|
144
154
|
return this._stubExecution(
|
|
145
155
|
'Epic 4 stub mode — real build is delegated to BuildOrchestrator outside the test runner',
|
|
146
156
|
{
|
|
147
|
-
|
|
157
|
+
// Honesty invariant (epic: empty-build-honesty): no `implementationPath`
|
|
158
|
+
// here — the plan path is not an implementation. The stub already reports
|
|
159
|
+
// success:false, and `codeChanges` carries whatever real files (if any)
|
|
160
|
+
// the subtasks touched.
|
|
148
161
|
planPath,
|
|
149
162
|
progress,
|
|
150
163
|
subtaskResults,
|
|
@@ -300,6 +300,69 @@ class GateEvaluator {
|
|
|
300
300
|
return null;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
+
/**
|
|
304
|
+
* Collect the REAL code files an epic actually wrote/modified.
|
|
305
|
+
*
|
|
306
|
+
* Honesty invariant (epic: empty-build-honesty): a plan file is NOT an
|
|
307
|
+
* implementation. This gathers the concrete files touched from `codeChanges` /
|
|
308
|
+
* `filesModified` (and `build.filesModified` when the build result is nested),
|
|
309
|
+
* then EXCLUDES the plan artifact (`planPath` / `implementationPath`) so a build
|
|
310
|
+
* that only produced a plan yields an empty list — and the caller can block it.
|
|
311
|
+
*
|
|
312
|
+
* Entries may be strings or `{ path | file }` objects; blanks and duplicates are
|
|
313
|
+
* dropped. Returns a de-duplicated array of file paths (never the plan itself).
|
|
314
|
+
*
|
|
315
|
+
* @param {Object} epicResult - Result from the source epic
|
|
316
|
+
* @returns {string[]} Real implementation files (plan artifact excluded)
|
|
317
|
+
* @private
|
|
318
|
+
*/
|
|
319
|
+
_collectImplementationFiles(epicResult) {
|
|
320
|
+
if (!epicResult || typeof epicResult !== 'object') {
|
|
321
|
+
return [];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const resolveSafe = (p) => {
|
|
325
|
+
try {
|
|
326
|
+
return path.resolve(p);
|
|
327
|
+
} catch {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// The plan artifact is explicitly NOT an implementation — exclude it.
|
|
333
|
+
const planPaths = new Set(
|
|
334
|
+
[epicResult.planPath, epicResult.implementationPath]
|
|
335
|
+
.filter((p) => typeof p === 'string' && p.trim() !== '')
|
|
336
|
+
.map(resolveSafe)
|
|
337
|
+
.filter(Boolean),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
const raw = []
|
|
341
|
+
.concat(Array.isArray(epicResult.codeChanges) ? epicResult.codeChanges : [])
|
|
342
|
+
.concat(Array.isArray(epicResult.filesModified) ? epicResult.filesModified : [])
|
|
343
|
+
.concat(
|
|
344
|
+
epicResult.build && Array.isArray(epicResult.build.filesModified)
|
|
345
|
+
? epicResult.build.filesModified
|
|
346
|
+
: [],
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const files = [];
|
|
350
|
+
for (const entry of raw) {
|
|
351
|
+
const file = typeof entry === 'string' ? entry : entry && (entry.path || entry.file);
|
|
352
|
+
if (typeof file !== 'string' || file.trim() === '') {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const resolved = resolveSafe(file);
|
|
356
|
+
if (resolved && planPaths.has(resolved)) {
|
|
357
|
+
continue; // a plan path masquerading as a code change — not implementation
|
|
358
|
+
}
|
|
359
|
+
if (!files.includes(file)) {
|
|
360
|
+
files.push(file);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return files;
|
|
364
|
+
}
|
|
365
|
+
|
|
303
366
|
/**
|
|
304
367
|
* Run a single check
|
|
305
368
|
* @private
|
|
@@ -380,11 +443,23 @@ class GateEvaluator {
|
|
|
380
443
|
break;
|
|
381
444
|
}
|
|
382
445
|
|
|
383
|
-
case 'implementation_exists':
|
|
384
|
-
|
|
385
|
-
|
|
446
|
+
case 'implementation_exists': {
|
|
447
|
+
// Honesty invariant (epic: empty-build-honesty): "implementation exists"
|
|
448
|
+
// MUST mean real code was written — not merely that a PLAN file exists. The
|
|
449
|
+
// multi-story measurement (2026-06-30) caught this gate APPROVING (score 5.0)
|
|
450
|
+
// a build that wrote ZERO code files: the old check trusted
|
|
451
|
+
// `implementationPath`, but epic-4 set that to the PLAN path (which ALWAYS
|
|
452
|
+
// exists), so an empty build slipped through. Base the check on the real list
|
|
453
|
+
// of files touched by the build, excluding the plan artifact itself. Empty
|
|
454
|
+
// build → zero real files → critical fail → gate BLOCKS (empty ≠ success).
|
|
455
|
+
const files = this._collectImplementationFiles(epicResult);
|
|
456
|
+
result.passed = files.length > 0;
|
|
457
|
+
result.message = result.passed
|
|
458
|
+
? `Implementation exists (${files.length} code file(s) changed)`
|
|
459
|
+
: 'No implementation found — build wrote zero code files (plan path is not implementation)';
|
|
386
460
|
result.severity = 'critical';
|
|
387
461
|
break;
|
|
462
|
+
}
|
|
388
463
|
|
|
389
464
|
case 'no_critical_errors': {
|
|
390
465
|
const criticalErrors = (epicResult.errors || []).filter((e) => e.severity === 'critical');
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# - SHA256 hashes for change detection
|
|
8
8
|
# - File types for categorization
|
|
9
9
|
#
|
|
10
|
-
version: 1.19.
|
|
10
|
+
version: 1.19.2
|
|
11
11
|
generator: scripts/generate-install-manifest.js
|
|
12
12
|
file_count: 1161
|
|
13
13
|
files:
|
|
@@ -496,9 +496,9 @@ files:
|
|
|
496
496
|
type: core
|
|
497
497
|
size: 34059
|
|
498
498
|
- path: core/execution/build-orchestrator.js
|
|
499
|
-
hash: sha256:
|
|
499
|
+
hash: sha256:6b1a213652493a7f453267d2f4f7937f5dac625cdfb95ca558747468922cb12f
|
|
500
500
|
type: core
|
|
501
|
-
size:
|
|
501
|
+
size: 41204
|
|
502
502
|
- path: core/execution/build-state-manager.js
|
|
503
503
|
hash: sha256:3b2fe56bf0e1a480f663a02afbb83c6d11cf89fb56815381c7ccafab948a63ae
|
|
504
504
|
type: core
|
|
@@ -992,9 +992,9 @@ files:
|
|
|
992
992
|
type: core
|
|
993
993
|
size: 9463
|
|
994
994
|
- path: core/orchestration/executors/epic-4-executor.js
|
|
995
|
-
hash: sha256:
|
|
995
|
+
hash: sha256:9aa50bd546d00d79e9ed2642377a66cdecef4f91c36440fde5ef617b18296226
|
|
996
996
|
type: core
|
|
997
|
-
size:
|
|
997
|
+
size: 11029
|
|
998
998
|
- path: core/orchestration/executors/epic-5-executor.js
|
|
999
999
|
hash: sha256:865224cd6cb1f80c1228e149349aea5c335440201c9047f849d2ec85b73d3f03
|
|
1000
1000
|
type: core
|
|
@@ -1016,9 +1016,9 @@ files:
|
|
|
1016
1016
|
type: core
|
|
1017
1017
|
size: 10211
|
|
1018
1018
|
- path: core/orchestration/gate-evaluator.js
|
|
1019
|
-
hash: sha256:
|
|
1019
|
+
hash: sha256:514c80ec7b305bf85d9816bca4ecdfc972faa84796a84794fef71d302a93f72b
|
|
1020
1020
|
type: core
|
|
1021
|
-
size:
|
|
1021
|
+
size: 23437
|
|
1022
1022
|
- path: core/orchestration/greenfield-handler.js
|
|
1023
1023
|
hash: sha256:330be2c76c3eb5a9a1f1c975837308802b8fc6fb871f9b584787ae4e2aa8fcb1
|
|
1024
1024
|
type: core
|
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.19.2] — 2026-07-01 — 🔒 Honestidade do gate (build vazio não passa) + aposta medida
|
|
11
|
+
|
|
12
|
+
> Patch. Atualização segura via `npx sinapse-ai update`. Fecha o último furo de honestidade do motor achado ao medir a aposta de orquestração multi-story.
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
- **orchestration** — o gate `epic4_to_epic6` aprovava (score 5.0) um build que não escreveu **nenhum arquivo**: o check `implementation_exists` aceitava `implementationPath`, que o epic-4 setava para o caminho do **plano** (sempre existe). Agora o check exige arquivos de código **reais** (`codeChanges` + `filesModified`, excluindo o plano) e bloqueia build vazio; o epic-4 não usa mais o `implementationPath` enganoso e o `BuildOrchestrator` expõe `filesModified` de verdade. Estende a invariante de honestidade — build "sucedido" sem arquivos ≠ sucesso (#317).
|
|
17
|
+
|
|
18
|
+
### Documentation
|
|
19
|
+
|
|
20
|
+
- **epic** — fecha o épico `orchestration-consolidation` com o **veredito medido do checkpoint "matar ou dobrar"**: HÍBRIDO. Na medição multi-story, o modo nativo entregou 3/3 stories em 64s e 1 chamada, enquanto o motor entregou 1/3 em ~13,5min (a coordenação multi-story corrompeu o estado entre stories). O motor é assumido como **assistente de story isolada** (spec + plano reais); a orquestração autônoma multi-story vira **limitação documentada** (`KNOWN-LIMITATIONS.md`), não promessa. Docs públicos já eram honestos (#318).
|
|
21
|
+
|
|
10
22
|
## [1.19.1] — 2026-06-30 — 🔧 Motor honesto ponta-a-ponta + Windows-safe
|
|
11
23
|
|
|
12
24
|
> Patch. Atualização segura via `npx sinapse-ai update`. Fecha 2 defeitos do motor expostos pelo checkpoint e2e do épico orchestration-consolidation.
|