agentic-workflow-manager 3.4.0 → 3.6.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/dist/src/commands/job/exec-wrapper.js +136 -0
- package/dist/src/commands/job/export.js +94 -0
- package/dist/src/commands/job/gate.js +118 -0
- package/dist/src/commands/job/heartbeat.js +15 -0
- package/dist/src/commands/job/index.js +246 -0
- package/dist/src/commands/job/query.js +37 -0
- package/dist/src/commands/job/reap.js +24 -0
- package/dist/src/commands/job/reconcile.js +112 -0
- package/dist/src/commands/job/request.js +27 -0
- package/dist/src/commands/sensors/exec.js +121 -0
- package/dist/src/commands/sensors/index.js +4 -4
- package/dist/src/commands/sensors/run.js +124 -71
- package/dist/src/commands/watch/apply.js +352 -0
- package/dist/src/commands/watch/generations.js +249 -0
- package/dist/src/commands/watch/index.js +49 -0
- package/dist/src/commands/watch/init.js +72 -0
- package/dist/src/commands/watch/lock.js +89 -0
- package/dist/src/commands/watch/runner.js +191 -0
- package/dist/src/commands/watch/supervisor.js +266 -0
- package/dist/src/core/atomic-file.js +31 -0
- package/dist/src/core/export/pack.js +7 -1
- package/dist/src/core/journal/adapter.js +27 -0
- package/dist/src/core/journal/fingerprint.js +80 -0
- package/dist/src/core/journal/paths.js +56 -0
- package/dist/src/core/journal/process.js +284 -0
- package/dist/src/core/journal/redact.js +142 -0
- package/dist/src/core/journal/requests.js +132 -0
- package/dist/src/core/journal/store.js +107 -0
- package/dist/src/core/journal/types.js +165 -0
- package/dist/src/index.js +4 -0
- package/dist/tests/commands/job/exec-wrapper.test.js +85 -0
- package/dist/tests/commands/job/export.test.js +76 -0
- package/dist/tests/commands/job/gate-reconcile.test.js +297 -0
- package/dist/tests/commands/job/reap-cli.test.js +101 -0
- package/dist/tests/commands/job/verbs.test.js +56 -0
- package/dist/tests/commands/job/verdict-determinism.test.js +138 -0
- package/dist/tests/commands/sensors/exec-fixtures.js +24 -0
- package/dist/tests/commands/sensors/exec.test.js +91 -0
- package/dist/tests/commands/sensors/run-inconclusive.test.js +55 -66
- package/dist/tests/commands/sensors/run-partial.test.js +225 -0
- package/dist/tests/commands/sensors/run-tool-missing.test.js +6 -6
- package/dist/tests/commands/sensors/run.test.js +64 -81
- package/dist/tests/commands/watch/apply.test.js +397 -0
- package/dist/tests/commands/watch/e2e-crash.test.js +157 -0
- package/dist/tests/commands/watch/generations.test.js +115 -0
- package/dist/tests/commands/watch/integration.test.js +124 -0
- package/dist/tests/commands/watch/lock.test.js +60 -0
- package/dist/tests/commands/watch/runner.test.js +239 -0
- package/dist/tests/commands/watch/supervisor-loop.test.js +203 -0
- package/dist/tests/commands/watch/watch-init.test.js +43 -0
- package/dist/tests/core/atomic-file-durable.test.js +42 -0
- package/dist/tests/core/journal/adapter.test.js +27 -0
- package/dist/tests/core/journal/fingerprint.test.js +164 -0
- package/dist/tests/core/journal/paths.test.js +35 -0
- package/dist/tests/core/journal/process.test.js +213 -0
- package/dist/tests/core/journal/redact.test.js +59 -0
- package/dist/tests/core/journal/requests.test.js +134 -0
- package/dist/tests/core/journal/store.test.js +88 -0
- package/dist/tests/core/journal/types.test.js +78 -0
- package/dist/tests/structural/exec-invocation-explicit-stdio.test.js +94 -0
- package/package.json +1 -1
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Structural regression guard for the recurring class of bug found across
|
|
3
|
+
// R1's durable-controller work (harness-retro, 2026-08-01 — r1-durable-controller
|
|
4
|
+
// plan, Task 20 + post-implementation-qa): execFileSync/spawn/spawnSync calls
|
|
5
|
+
// with no explicit `stdio` option inherit Node's default `inheritStderr`
|
|
6
|
+
// relay, which pipes the SUBPROCESS's stderr onto the CALLING process's own
|
|
7
|
+
// stderr fd. When that fd is a destroyed/broken pipe (a detached wrapper
|
|
8
|
+
// whose parent already tore down its own pipes, a supervisor whose stdout
|
|
9
|
+
// went away), the relay itself raises an async EPIPE with no listener,
|
|
10
|
+
// which Node re-throws — silently crashing the caller before it can finish
|
|
11
|
+
// its work.
|
|
12
|
+
//
|
|
13
|
+
// The bug recurred FIVE separate times in the same session even after the
|
|
14
|
+
// first fix landed: spawnStructured's own detached-child spawn, then 4 more
|
|
15
|
+
// bare execFileSync('git'/'ps'/'pgrep', ...) call sites in DIFFERENT files
|
|
16
|
+
// (process.ts, lock.ts, job/index.ts, watch/index.ts, fingerprint.ts) plus a
|
|
17
|
+
// bonus spawnSync('zip', ...) in pack.ts — because each fix was scoped to
|
|
18
|
+
// the one call site a reviewer happened to be looking at, not the whole
|
|
19
|
+
// class. This test exercises the whole class in one pass: every
|
|
20
|
+
// execFileSync/spawn/spawnSync call site anywhere under cli/src must pass an
|
|
21
|
+
// explicit `stdio` option (or the shared `EXEC_STDIO` constant) — so a
|
|
22
|
+
// future call site added without one fails immediately instead of waiting
|
|
23
|
+
// for the next EPIPE crash to be noticed.
|
|
24
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
25
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
const fs_1 = __importDefault(require("fs"));
|
|
29
|
+
const path_1 = __importDefault(require("path"));
|
|
30
|
+
const SRC_ROOT = path_1.default.join(__dirname, '../../src');
|
|
31
|
+
const CALLEE_NAMES = ['execFileSync', 'spawnSync', 'spawn'];
|
|
32
|
+
function listTsFiles(dir) {
|
|
33
|
+
const out = [];
|
|
34
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
35
|
+
const full = path_1.default.join(dir, entry.name);
|
|
36
|
+
if (entry.isDirectory())
|
|
37
|
+
out.push(...listTsFiles(full));
|
|
38
|
+
else if (entry.isFile() && entry.name.endsWith('.ts'))
|
|
39
|
+
out.push(full);
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
// Strips comments before scanning: JSDoc/inline comments in this codebase
|
|
44
|
+
// routinely reference "spawn (...)" or "execFileSync(...)" in prose (e.g.
|
|
45
|
+
// process.ts's own EPIPE-hardening comment), which would otherwise register
|
|
46
|
+
// as phantom call sites. Naive block/line-comment stripping, not a full TS
|
|
47
|
+
// parser — sufficient for a structural guard, and a false positive here
|
|
48
|
+
// fails loud rather than silently missing a real site. The `[^:]` guard on
|
|
49
|
+
// `//` avoids treating a `https://` URL in a string as a line comment.
|
|
50
|
+
function stripComments(source) {
|
|
51
|
+
return source
|
|
52
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
53
|
+
.replace(/(^|[^:])\/\/.*$/gm, '$1');
|
|
54
|
+
}
|
|
55
|
+
// Extracts the paren-balanced argument text of every call to one of
|
|
56
|
+
// CALLEE_NAMES in `source` — e.g. "execFileSync('git', [...], {...})" -> the
|
|
57
|
+
// text between the outer parens. Balanced-paren scanning (not a single-line
|
|
58
|
+
// regex) is required because real call sites in this repo span multiple
|
|
59
|
+
// lines (e.g. process.ts's pgrep call).
|
|
60
|
+
function extractCalls(rawSource) {
|
|
61
|
+
const source = stripComments(rawSource);
|
|
62
|
+
const calls = [];
|
|
63
|
+
const calleeRe = new RegExp(`\\b(${CALLEE_NAMES.join('|')})\\s*\\(`, 'g');
|
|
64
|
+
let m;
|
|
65
|
+
while ((m = calleeRe.exec(source)) !== null) {
|
|
66
|
+
const start = m.index + m[0].length; // just after the opening '('
|
|
67
|
+
let depth = 1;
|
|
68
|
+
let i = start;
|
|
69
|
+
while (i < source.length && depth > 0) {
|
|
70
|
+
if (source[i] === '(')
|
|
71
|
+
depth++;
|
|
72
|
+
else if (source[i] === ')')
|
|
73
|
+
depth--;
|
|
74
|
+
i++;
|
|
75
|
+
}
|
|
76
|
+
calls.push({ callee: m[1], args: source.slice(start, i - 1) });
|
|
77
|
+
}
|
|
78
|
+
return calls;
|
|
79
|
+
}
|
|
80
|
+
describe('execFileSync/spawn/spawnSync invocations — explicit stdio required (structural)', () => {
|
|
81
|
+
it('every call site under cli/src passes an explicit stdio option, never relying on Node defaults', () => {
|
|
82
|
+
const violations = [];
|
|
83
|
+
for (const file of listTsFiles(SRC_ROOT)) {
|
|
84
|
+
const source = fs_1.default.readFileSync(file, 'utf8');
|
|
85
|
+
for (const call of extractCalls(source)) {
|
|
86
|
+
const hasExplicitStdio = /\bstdio\s*:/.test(call.args) || /\bEXEC_STDIO\b/.test(call.args);
|
|
87
|
+
if (!hasExplicitStdio) {
|
|
88
|
+
violations.push(`${path_1.default.relative(SRC_ROOT, file)}: ${call.callee}(...) has no explicit stdio option`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
expect(violations).toEqual([]);
|
|
93
|
+
});
|
|
94
|
+
});
|