@ryanfw/prompt-orchestration-pipeline 1.3.2 → 1.3.4
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/docs/http-api.md +80 -4
- package/package.json +1 -4
- package/src/api/__tests__/index.test.ts +443 -32
- package/src/api/index.ts +108 -127
- package/src/cli/__tests__/analyze-task.test.ts +40 -7
- package/src/cli/__tests__/index.test.ts +337 -17
- package/src/cli/__tests__/types.test.ts +0 -18
- package/src/cli/__tests__/update-pipeline-json.test.ts +19 -9
- package/src/cli/analyze-task.ts +3 -14
- package/src/cli/index.ts +73 -61
- package/src/cli/types.ts +0 -13
- package/src/cli/update-pipeline-json.ts +3 -14
- package/src/config/__tests__/paths.test.ts +46 -1
- package/src/config/__tests__/pipeline-registry.test.ts +767 -0
- package/src/config/__tests__/sse-events.test.ts +470 -0
- package/src/config/__tests__/statuses.test.ts +41 -0
- package/src/config/paths.ts +11 -2
- package/src/config/pipeline-registry.ts +258 -0
- package/src/config/sse-events.ts +122 -0
- package/src/config/statuses.ts +20 -1
- package/src/core/__tests__/agent-step.test.ts +115 -0
- package/src/core/__tests__/config.test.ts +545 -105
- package/src/core/__tests__/core-boot-resolution.test.ts +181 -0
- package/src/core/__tests__/job-concurrency.test.ts +260 -0
- package/src/core/__tests__/job-submission.test.ts +396 -0
- package/src/core/__tests__/job-view.test.ts +1341 -0
- package/src/core/__tests__/json-file.test.ts +111 -0
- package/src/core/__tests__/lifecycle-policy.test.ts +81 -7
- package/src/core/__tests__/logger.test.ts +22 -0
- package/src/core/__tests__/orchestrator-no-deprecated-exports.test.ts +41 -0
- package/src/core/__tests__/orchestrator.test.ts +615 -2
- package/src/core/__tests__/pipeline-runner.test.ts +946 -9
- package/src/core/__tests__/redact.test.ts +153 -0
- package/src/core/__tests__/runner-liveness.test.ts +910 -0
- package/src/core/__tests__/seed-naming.test.ts +57 -0
- package/src/core/__tests__/single-derivation.test.ts +159 -0
- package/src/core/__tests__/task-runner.test.ts +594 -3
- package/src/core/__tests__/task-telemetry.test.ts +241 -0
- package/src/core/__tests__/workspace-root-resolution-guard.test.ts +62 -0
- package/src/core/agent-step.ts +4 -1
- package/src/core/agent-types.ts +5 -4
- package/src/core/config.ts +134 -222
- package/src/core/control.ts +3 -0
- package/src/core/job-concurrency.ts +56 -20
- package/src/core/job-submission.ts +133 -0
- package/src/core/job-view.ts +473 -0
- package/src/core/json-file.ts +45 -0
- package/src/core/lifecycle-policy.ts +23 -8
- package/src/core/logger.ts +0 -29
- package/src/core/orchestrator.ts +200 -74
- package/src/core/pipeline-runner.ts +85 -53
- package/src/core/redact.ts +40 -0
- package/src/core/runner-liveness.ts +280 -0
- package/src/core/seed-naming.ts +9 -0
- package/src/core/status-writer.ts +27 -33
- package/src/core/task-runner.ts +356 -319
- package/src/core/task-telemetry.ts +107 -0
- package/src/harness/__tests__/subprocess.test.ts +112 -1
- package/src/harness/subprocess.ts +55 -16
- package/src/llm/__tests__/index.test.ts +684 -33
- package/src/llm/index.ts +310 -67
- package/src/providers/__tests__/alibaba.test.ts +67 -6
- package/src/providers/__tests__/anthropic.test.ts +35 -14
- package/src/providers/__tests__/base.test.ts +62 -0
- package/src/providers/__tests__/claude-code.test.ts +99 -14
- package/src/providers/__tests__/deepseek.test.ts +16 -6
- package/src/providers/__tests__/gemini.test.ts +47 -25
- package/src/providers/__tests__/moonshot.test.ts +27 -0
- package/src/providers/__tests__/openai.test.ts +262 -74
- package/src/providers/__tests__/opencode.test.ts +77 -0
- package/src/providers/__tests__/request-timeout-contract.test.ts +226 -0
- package/src/providers/__tests__/stream-accumulator.test.ts +52 -0
- package/src/providers/__tests__/types.test.ts +85 -0
- package/src/providers/__tests__/zero-fill-guard.test.ts +62 -0
- package/src/providers/__tests__/zhipu.test.ts +27 -14
- package/src/providers/alibaba.ts +20 -39
- package/src/providers/anthropic.ts +23 -11
- package/src/providers/base.ts +19 -0
- package/src/providers/claude-code.ts +27 -18
- package/src/providers/deepseek.ts +9 -28
- package/src/providers/gemini.ts +20 -58
- package/src/providers/moonshot.ts +15 -11
- package/src/providers/openai.ts +79 -61
- package/src/providers/opencode.ts +16 -33
- package/src/providers/stream-accumulator.ts +27 -21
- package/src/providers/types.ts +29 -4
- package/src/providers/zhipu.ts +15 -44
- package/src/task-analysis/__tests__/analyzer.test.ts +0 -0
- package/src/task-analysis/__tests__/enrichers-schema-deducer.test.ts +34 -2
- package/src/task-analysis/__tests__/no-ast-imports.test.ts +52 -0
- package/src/task-analysis/__tests__/repository.test.ts +65 -0
- package/src/task-analysis/__tests__/types.test.ts +63 -130
- package/src/task-analysis/analyzer.ts +91 -0
- package/src/task-analysis/enrichers/schema-deducer.ts +13 -2
- package/src/task-analysis/index.ts +2 -36
- package/src/task-analysis/repository.ts +45 -0
- package/src/task-analysis/types.ts +42 -58
- package/src/ui/client/__tests__/api.test.ts +143 -1
- package/src/ui/client/__tests__/bootstrap.test.ts +178 -62
- package/src/ui/client/__tests__/job-adapter.test.ts +203 -2
- package/src/ui/client/__tests__/load-state.test.ts +70 -0
- package/src/ui/client/__tests__/types.test.ts +66 -3
- package/src/ui/client/__tests__/useJobDetailWithUpdates.test.ts +390 -77
- package/src/ui/client/__tests__/useJobList.test.ts +198 -23
- package/src/ui/client/__tests__/useJobListWithUpdates.test.ts +218 -7
- package/src/ui/client/adapters/__tests__/job-adapter.test.ts +186 -0
- package/src/ui/client/adapters/job-adapter.ts +41 -16
- package/src/ui/client/api.ts +38 -15
- package/src/ui/client/bootstrap.ts +19 -14
- package/src/ui/client/hooks/useJobDetailWithUpdates.ts +73 -97
- package/src/ui/client/hooks/useJobList.ts +26 -31
- package/src/ui/client/hooks/useJobListWithUpdates.ts +42 -76
- package/src/ui/client/load-state.ts +20 -0
- package/src/ui/client/reducers/__tests__/job-events.test.ts +568 -0
- package/src/ui/client/reducers/job-events.ts +137 -0
- package/src/ui/client/types.ts +16 -20
- package/src/ui/components/DAGGrid.tsx +6 -1
- package/src/ui/components/JobDetail.tsx +12 -4
- package/src/ui/components/JobTable.tsx +41 -13
- package/src/ui/components/StageTimeline.tsx +8 -20
- package/src/ui/components/TaskAnalysisDisplay.tsx +48 -6
- package/src/ui/components/__tests__/DAGGrid.test.tsx +36 -0
- package/src/ui/components/__tests__/JobDetail.test.tsx +112 -0
- package/src/ui/components/__tests__/JobTable.test.tsx +137 -1
- package/src/ui/components/__tests__/StageTimeline.test.tsx +5 -6
- package/src/ui/components/__tests__/TaskAnalysisDisplay.test.tsx +64 -0
- package/src/ui/components/types.ts +35 -15
- package/src/ui/dist/assets/{index--RH3sAt3.js → index-DN3-zvtP.js} +4324 -263
- package/src/ui/dist/assets/index-DN3-zvtP.js.map +1 -0
- package/src/ui/dist/assets/style-CtZBnjlR.css +2 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/pages/PipelineDetail.tsx +60 -4
- package/src/ui/pages/PromptPipelineDashboard.tsx +59 -7
- package/src/ui/pages/__tests__/PromptPipelineDashboard.test.tsx +114 -32
- package/src/ui/pages/__tests__/pages.test.tsx +236 -42
- package/src/ui/server/__tests__/concurrency-endpoint.test.ts +54 -0
- package/src/ui/server/__tests__/config-bridge-node.test.ts +34 -1
- package/src/ui/server/__tests__/config-bridge.test.ts +9 -1
- package/src/ui/server/__tests__/embedded-assets.test.ts +66 -0
- package/src/ui/server/__tests__/file-endpoints.test.ts +183 -5
- package/src/ui/server/__tests__/gate-endpoints.test.ts +55 -0
- package/src/ui/server/__tests__/index.test.ts +63 -0
- package/src/ui/server/__tests__/job-control-endpoints.test.ts +512 -2
- package/src/ui/server/__tests__/job-endpoints.test.ts +158 -2
- package/src/ui/server/__tests__/read-static-path-guard.test.ts +94 -0
- package/src/ui/server/__tests__/router-root-isolation.test.ts +204 -0
- package/src/ui/server/__tests__/router-threading.test.ts +148 -0
- package/src/ui/server/__tests__/router.test.ts +104 -0
- package/src/ui/server/__tests__/sse-broadcast.test.ts +44 -0
- package/src/ui/server/__tests__/sse-enhancer.test.ts +70 -0
- package/src/ui/server/config-bridge-node.ts +8 -26
- package/src/ui/server/config-bridge.ts +3 -4
- package/src/ui/server/embedded-assets-imports.d.ts +44 -0
- package/src/ui/server/embedded-assets.ts +13 -2
- package/src/ui/server/endpoints/__tests__/meta-endpoint.test.ts +1 -0
- package/src/ui/server/endpoints/__tests__/pipeline-analysis-endpoint.test.ts +167 -0
- package/src/ui/server/endpoints/__tests__/schema-file-endpoint.test.ts +104 -0
- package/src/ui/server/endpoints/__tests__/task-analysis-endpoint.test.ts +103 -0
- package/src/ui/server/endpoints/__tests__/upload-endpoints.test.ts +162 -96
- package/src/ui/server/endpoints/concurrency-endpoint.ts +2 -1
- package/src/ui/server/endpoints/create-pipeline-endpoint.ts +14 -29
- package/src/ui/server/endpoints/file-endpoints.ts +15 -10
- package/src/ui/server/endpoints/gate-endpoints.ts +2 -6
- package/src/ui/server/endpoints/job-control-endpoints.ts +129 -141
- package/src/ui/server/endpoints/job-endpoints.ts +7 -0
- package/src/ui/server/endpoints/meta-endpoint.ts +1 -1
- package/src/ui/server/endpoints/pipeline-analysis-endpoint.ts +99 -11
- package/src/ui/server/endpoints/schema-file-endpoint.ts +11 -3
- package/src/ui/server/endpoints/task-analysis-endpoint.ts +21 -5
- package/src/ui/server/endpoints/task-save-endpoint.ts +1 -2
- package/src/ui/server/endpoints/upload-endpoints.ts +5 -40
- package/src/ui/server/index.ts +19 -14
- package/src/ui/server/job-reader.ts +14 -2
- package/src/ui/server/router.ts +33 -10
- package/src/ui/server/sse-broadcast.ts +14 -3
- package/src/ui/server/sse-enhancer.ts +12 -2
- package/src/ui/state/__tests__/schema-loader.test.ts +11 -1
- package/src/ui/state/__tests__/snapshot.test.ts +120 -14
- package/src/ui/state/__tests__/types.test.ts +104 -5
- package/src/ui/state/snapshot.ts +2 -2
- package/src/ui/state/transformers/__tests__/list-transformer.test.ts +85 -2
- package/src/ui/state/transformers/__tests__/status-transformer.test.ts +250 -22
- package/src/ui/state/transformers/list-transformer.ts +13 -3
- package/src/ui/state/transformers/status-transformer.ts +36 -170
- package/src/ui/state/types.ts +15 -48
- package/src/utils/__tests__/path-containment.test.ts +160 -0
- package/src/{ui/server/utils → utils}/path-containment.ts +21 -0
- package/src/task-analysis/__tests__/enrichers-analysis-writer.test.ts +0 -86
- package/src/task-analysis/__tests__/enrichers-artifact-resolver.test.ts +0 -124
- package/src/task-analysis/__tests__/extractors-artifacts.test.ts +0 -133
- package/src/task-analysis/__tests__/extractors-llm-calls.test.ts +0 -46
- package/src/task-analysis/__tests__/extractors-stages.test.ts +0 -52
- package/src/task-analysis/__tests__/index.test.ts +0 -143
- package/src/task-analysis/__tests__/parser.test.ts +0 -41
- package/src/task-analysis/__tests__/utils-ast.test.ts +0 -82
- package/src/task-analysis/enrichers/analysis-writer.ts +0 -75
- package/src/task-analysis/enrichers/artifact-resolver.ts +0 -89
- package/src/task-analysis/extractors/artifacts.ts +0 -143
- package/src/task-analysis/extractors/llm-calls.ts +0 -117
- package/src/task-analysis/extractors/stages.ts +0 -45
- package/src/task-analysis/parser.ts +0 -20
- package/src/task-analysis/utils/ast.ts +0 -45
- package/src/ui/dist/assets/index--RH3sAt3.js.map +0 -1
- package/src/ui/dist/assets/style-CSSKuMOe.css +0 -2
- package/src/ui/embedded-assets.js +0 -12
- package/src/ui/server/__tests__/path-containment.test.ts +0 -54
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { chmod, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { JsonFileError, readJsonFile, writeJsonFile } from "../json-file";
|
|
7
|
+
|
|
8
|
+
describe("readJsonFile", () => {
|
|
9
|
+
let dir: string;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
dir = await mkdtemp(join(tmpdir(), "pop-json-file-test-"));
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterEach(async () => {
|
|
16
|
+
await rm(dir, { recursive: true, force: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("returns the parsed typed value for a valid JSON file", async () => {
|
|
20
|
+
const path = join(dir, "valid.json");
|
|
21
|
+
await writeFile(path, JSON.stringify({ name: "demo", count: 3 }));
|
|
22
|
+
|
|
23
|
+
const value = await readJsonFile<{ name: string; count: number }>(path);
|
|
24
|
+
|
|
25
|
+
expect(value).toEqual({ name: "demo", count: 3 });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("throws JsonFileError reason 'parse' carrying path and cause for malformed JSON, leaving the file unchanged", async () => {
|
|
29
|
+
const path = join(dir, "bad.json");
|
|
30
|
+
const original = "{ not valid json ";
|
|
31
|
+
await writeFile(path, original);
|
|
32
|
+
|
|
33
|
+
let thrown: unknown;
|
|
34
|
+
try {
|
|
35
|
+
await readJsonFile(path);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
thrown = err;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
expect(thrown).toBeInstanceOf(JsonFileError);
|
|
41
|
+
const error = thrown as JsonFileError;
|
|
42
|
+
expect(error.reason).toBe("parse");
|
|
43
|
+
expect(error.path).toBe(path);
|
|
44
|
+
expect(error.cause).toBeInstanceOf(SyntaxError);
|
|
45
|
+
expect(error.message).toContain(path);
|
|
46
|
+
expect(error.message).toContain("parse");
|
|
47
|
+
expect(error.message).toContain((error.cause as Error).message);
|
|
48
|
+
|
|
49
|
+
expect(await readFile(path, "utf8")).toBe(original);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("throws JsonFileError reason 'missing' with path and ENOENT cause for a nonexistent file", async () => {
|
|
53
|
+
const path = join(dir, "absent.json");
|
|
54
|
+
|
|
55
|
+
let thrown: unknown;
|
|
56
|
+
try {
|
|
57
|
+
await readJsonFile(path);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
thrown = err;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
expect(thrown).toBeInstanceOf(JsonFileError);
|
|
63
|
+
const error = thrown as JsonFileError;
|
|
64
|
+
expect(error.reason).toBe("missing");
|
|
65
|
+
expect(error.path).toBe(path);
|
|
66
|
+
expect(error.message).toContain(path);
|
|
67
|
+
expect(error.message).toContain("missing");
|
|
68
|
+
expect(error.message).toContain("ENOENT");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("writeJsonFile", () => {
|
|
73
|
+
let dir: string;
|
|
74
|
+
|
|
75
|
+
beforeEach(async () => {
|
|
76
|
+
dir = await mkdtemp(join(tmpdir(), "pop-json-file-test-"));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
afterEach(async () => {
|
|
80
|
+
await rm(dir, { recursive: true, force: true });
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("round-trips a value and ends the file with a trailing newline", async () => {
|
|
84
|
+
const path = join(dir, "out.json");
|
|
85
|
+
const value = { name: "demo", tasks: ["a", "b"] };
|
|
86
|
+
|
|
87
|
+
await writeJsonFile(path, value);
|
|
88
|
+
|
|
89
|
+
const text = await readFile(path, "utf8");
|
|
90
|
+
expect(text.endsWith("\n")).toBe(true);
|
|
91
|
+
expect(JSON.parse(text)).toEqual(value);
|
|
92
|
+
expect(await readJsonFile<typeof value>(path)).toEqual(value);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("leaves the original file intact when the write fails before rename", async () => {
|
|
96
|
+
const path = join(dir, "config.json");
|
|
97
|
+
const original = JSON.stringify({ keep: true }) + "\n";
|
|
98
|
+
await writeFile(path, original);
|
|
99
|
+
|
|
100
|
+
// Make the directory read-only so atomicWrite's temp-file write fails
|
|
101
|
+
// before it can rename over the original target.
|
|
102
|
+
await chmod(dir, 0o500);
|
|
103
|
+
try {
|
|
104
|
+
await expect(writeJsonFile(path, { partial: true })).rejects.toThrow();
|
|
105
|
+
} finally {
|
|
106
|
+
await chmod(dir, 0o700);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
expect(await readFile(path, "utf8")).toBe(original);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { describe, test, expect } from "bun:test";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
decideTransition,
|
|
4
|
+
areDependenciesSatisfied,
|
|
5
|
+
firstUnsatisfiedDependency,
|
|
6
|
+
} from "../lifecycle-policy";
|
|
3
7
|
|
|
4
8
|
describe("decideTransition", () => {
|
|
5
9
|
test("start with pending task and ready dependencies returns ok", () => {
|
|
@@ -24,25 +28,25 @@ describe("decideTransition", () => {
|
|
|
24
28
|
});
|
|
25
29
|
});
|
|
26
30
|
|
|
27
|
-
test("restart
|
|
31
|
+
test("restart with ready dependencies returns ok", () => {
|
|
28
32
|
const result = decideTransition({
|
|
29
33
|
op: "restart",
|
|
30
34
|
taskState: "done",
|
|
31
|
-
dependenciesReady:
|
|
35
|
+
dependenciesReady: true,
|
|
32
36
|
});
|
|
33
37
|
expect(result).toEqual({ ok: true });
|
|
34
38
|
});
|
|
35
39
|
|
|
36
|
-
test("restart
|
|
40
|
+
test("restart with unready dependencies is blocked by dependencies", () => {
|
|
37
41
|
const result = decideTransition({
|
|
38
42
|
op: "restart",
|
|
39
|
-
taskState: "
|
|
40
|
-
dependenciesReady:
|
|
43
|
+
taskState: "done",
|
|
44
|
+
dependenciesReady: false,
|
|
41
45
|
});
|
|
42
46
|
expect(result).toEqual({
|
|
43
47
|
ok: false,
|
|
44
48
|
code: "unsupported_lifecycle",
|
|
45
|
-
reason: "
|
|
49
|
+
reason: "dependencies",
|
|
46
50
|
});
|
|
47
51
|
});
|
|
48
52
|
|
|
@@ -76,3 +80,73 @@ describe("decideTransition", () => {
|
|
|
76
80
|
).toThrow(/Invalid dependenciesReady/);
|
|
77
81
|
});
|
|
78
82
|
});
|
|
83
|
+
|
|
84
|
+
describe("areDependenciesSatisfied", () => {
|
|
85
|
+
test("empty predecessor set is satisfied", () => {
|
|
86
|
+
expect(areDependenciesSatisfied([])).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("single done predecessor is satisfied", () => {
|
|
90
|
+
expect(areDependenciesSatisfied(["done"])).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("single skipped predecessor is satisfied", () => {
|
|
94
|
+
expect(areDependenciesSatisfied(["skipped"])).toBe(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("done and skipped together are satisfied", () => {
|
|
98
|
+
expect(areDependenciesSatisfied(["done", "skipped"])).toBe(true);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("pending predecessor is not satisfied", () => {
|
|
102
|
+
expect(areDependenciesSatisfied(["pending"])).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("running predecessor is not satisfied", () => {
|
|
106
|
+
expect(areDependenciesSatisfied(["running"])).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("failed predecessor is not satisfied", () => {
|
|
110
|
+
expect(areDependenciesSatisfied(["failed"])).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("normalizes case and synonyms: DONE and succeeded satisfy", () => {
|
|
114
|
+
expect(areDependenciesSatisfied(["DONE", "succeeded"])).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("normalizes synonyms: error maps to failed and does not satisfy", () => {
|
|
118
|
+
expect(areDependenciesSatisfied(["error"])).toBe(false);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe("firstUnsatisfiedDependency", () => {
|
|
123
|
+
test("empty predecessor list returns null", () => {
|
|
124
|
+
expect(firstUnsatisfiedDependency([])).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("all satisfied returns null", () => {
|
|
128
|
+
expect(
|
|
129
|
+
firstUnsatisfiedDependency([
|
|
130
|
+
{ name: "a", state: "done" },
|
|
131
|
+
{ name: "b", state: "skipped" },
|
|
132
|
+
]),
|
|
133
|
+
).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("returns the first offending predecessor", () => {
|
|
137
|
+
expect(
|
|
138
|
+
firstUnsatisfiedDependency([
|
|
139
|
+
{ name: "a", state: "done" },
|
|
140
|
+
{ name: "b", state: "pending" },
|
|
141
|
+
{ name: "c", state: "failed" },
|
|
142
|
+
]),
|
|
143
|
+
).toEqual({ name: "b", state: "pending" });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("returns the first element when it offends", () => {
|
|
147
|
+
expect(firstUnsatisfiedDependency([{ name: "a", state: "running" }])).toEqual({
|
|
148
|
+
name: "a",
|
|
149
|
+
state: "running",
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -46,6 +46,28 @@ describe("createLogger", () => {
|
|
|
46
46
|
const logger = createLogger("Test");
|
|
47
47
|
expect(() => logger.sse("event", { data: "test" })).not.toThrow();
|
|
48
48
|
});
|
|
49
|
+
|
|
50
|
+
test("sse performs no registry broadcast", () => {
|
|
51
|
+
const spy = mock(() => {});
|
|
52
|
+
console.log = spy;
|
|
53
|
+
const logger = createLogger("Test");
|
|
54
|
+
logger.sse("event", { data: "test" });
|
|
55
|
+
const calls = spy.mock.calls;
|
|
56
|
+
expect(calls.length).toBe(1);
|
|
57
|
+
expect(calls[0]!.join(" ")).toContain("[SSE:event]");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("logger.ts and status-writer.ts no longer reference the dead SSE machinery", async () => {
|
|
61
|
+
const loggerSrc = await Bun.file(new URL("../logger.ts", import.meta.url)).text();
|
|
62
|
+
const statusWriterSrc = await Bun.file(new URL("../status-writer.ts", import.meta.url)).text();
|
|
63
|
+
for (const needle of ["getSSERegistry", "../ui/sse.ts", "logger.sse("]) {
|
|
64
|
+
expect(loggerSrc).not.toContain(needle);
|
|
65
|
+
expect(statusWriterSrc).not.toContain(needle);
|
|
66
|
+
}
|
|
67
|
+
for (const needle of ["task:updated", "state:change", "lifecycle_block"]) {
|
|
68
|
+
expect(statusWriterSrc).not.toContain(needle);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
49
71
|
});
|
|
50
72
|
|
|
51
73
|
describe("createJobLogger", () => {
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
// Source scan modeled on the deleted-config-symbols scan
|
|
6
|
+
// (src/core/__tests__/config.test.ts "source deletion scan" block).
|
|
7
|
+
// Guards AC-11: handleSeedAdd and SEED_PATTERN must be absent from src/ after step 10.
|
|
8
|
+
const DELETED_TOKENS = ["handleSeedAdd", "SEED_PATTERN"] as const;
|
|
9
|
+
|
|
10
|
+
const SRC_ROOT = join(import.meta.dir, "..", "..");
|
|
11
|
+
|
|
12
|
+
function walkTs(dir: string): string[] {
|
|
13
|
+
const out: string[] = [];
|
|
14
|
+
for (const entry of readdirSync(dir)) {
|
|
15
|
+
const full = join(dir, entry);
|
|
16
|
+
if (statSync(full).isDirectory()) {
|
|
17
|
+
out.push(...walkTs(full));
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (/\.tsx?$/.test(entry)) out.push(full);
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("orchestrator dead-code scan (AC-11)", () => {
|
|
26
|
+
test("no src/ file references handleSeedAdd or SEED_PATTERN", () => {
|
|
27
|
+
const files = walkTs(SRC_ROOT).filter((f) => !f.includes("/__tests__/"));
|
|
28
|
+
const offending: Array<{ file: string; token: string }> = [];
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
const content = readFileSync(file, "utf8");
|
|
31
|
+
for (const token of DELETED_TOKENS) {
|
|
32
|
+
if (content.includes(token)) offending.push({ file, token });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (offending.length > 0) {
|
|
36
|
+
const lines = offending.map((o) => ` ${o.file}: ${o.token}`).join("\n");
|
|
37
|
+
throw new Error(`Deleted orchestrator symbols still referenced under src/:\n${lines}`);
|
|
38
|
+
}
|
|
39
|
+
expect(offending).toHaveLength(0);
|
|
40
|
+
});
|
|
41
|
+
});
|