@united-workforce/cli 0.3.0 → 0.5.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/README.md +45 -11
- package/dist/.build-fingerprint +1 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js +17 -7
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
- package/dist/__tests__/build-step-entry.test.d.ts +2 -0
- package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
- package/dist/__tests__/build-step-entry.test.js +173 -0
- package/dist/__tests__/build-step-entry.test.js.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
- package/dist/__tests__/concurrency.test.d.ts +2 -0
- package/dist/__tests__/concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/concurrency.test.js +196 -0
- package/dist/__tests__/concurrency.test.js.map +1 -0
- package/dist/__tests__/config.test.js +26 -302
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/current-role.test.js +7 -6
- package/dist/__tests__/current-role.test.js.map +1 -1
- package/dist/__tests__/e2e-mock-agent.test.js +43 -30
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/format-text-default.test.d.ts +2 -0
- package/dist/__tests__/format-text-default.test.d.ts.map +1 -0
- package/dist/__tests__/format-text-default.test.js +43 -0
- package/dist/__tests__/format-text-default.test.js.map +1 -0
- package/dist/__tests__/format-text-registry.test.d.ts +2 -0
- package/dist/__tests__/format-text-registry.test.d.ts.map +1 -0
- package/dist/__tests__/format-text-registry.test.js +158 -0
- package/dist/__tests__/format-text-registry.test.js.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
- package/dist/__tests__/log-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/log-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/log-text-renderer.test.js +265 -0
- package/dist/__tests__/log-text-renderer.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +9 -50
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
- package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts +2 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts.map +1 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.js +102 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.js.map +1 -0
- package/dist/__tests__/output-mapper-workflow-add.test.d.ts +2 -0
- package/dist/__tests__/output-mapper-workflow-add.test.d.ts.map +1 -0
- package/dist/__tests__/output-mapper-workflow-add.test.js +22 -0
- package/dist/__tests__/output-mapper-workflow-add.test.js.map +1 -0
- package/dist/__tests__/pid-recycling.test.d.ts +2 -0
- package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
- package/dist/__tests__/pid-recycling.test.js +273 -0
- package/dist/__tests__/pid-recycling.test.js.map +1 -0
- package/dist/__tests__/prompt.test.js +365 -2
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/resolve-head-hash.test.js +12 -4
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
- package/dist/__tests__/setup-agent-discovery.test.js +21 -30
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
- package/dist/__tests__/setup-complexity.test.js +2 -168
- package/dist/__tests__/setup-complexity.test.js.map +1 -1
- package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
- package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
- package/dist/__tests__/setup-no-llm.test.js +52 -0
- package/dist/__tests__/setup-no-llm.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +27 -28
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.d.ts +2 -0
- package/dist/__tests__/step-ask.test.d.ts.map +1 -0
- package/dist/__tests__/step-ask.test.js +507 -0
- package/dist/__tests__/step-ask.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.js +1 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -1
- package/dist/__tests__/step-timing.test.js +2 -0
- package/dist/__tests__/step-timing.test.js.map +1 -1
- package/dist/__tests__/store-global-cas.test.js +2 -2
- package/dist/__tests__/store-global-cas.test.js.map +1 -1
- package/dist/__tests__/store-unified-threads.test.js +28 -26
- package/dist/__tests__/store-unified-threads.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-status.test.js +25 -19
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.js +110 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.js.map +1 -0
- package/dist/__tests__/thread-list-filters.test.js +354 -17
- package/dist/__tests__/thread-list-filters.test.js.map +1 -1
- package/dist/__tests__/thread-list-template-ms-date.test.d.ts +2 -0
- package/dist/__tests__/thread-list-template-ms-date.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js +102 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts +2 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.js +157 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.js.map +1 -0
- package/dist/__tests__/thread-poke.test.d.ts +2 -0
- package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
- package/dist/__tests__/thread-poke.test.js +422 -0
- package/dist/__tests__/thread-poke.test.js.map +1 -0
- package/dist/__tests__/thread-read-xml-tags.test.js +10 -9
- package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -1
- package/dist/__tests__/thread-resume.test.js +21 -15
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-show-status.test.js +17 -28
- package/dist/__tests__/thread-show-status.test.js.map +1 -1
- package/dist/__tests__/thread-start-cwd-cli.test.js +15 -3
- package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -1
- package/dist/__tests__/thread-stop-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/thread-stop-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/thread-stop-text-renderer.test.js +148 -0
- package/dist/__tests__/thread-stop-text-renderer.test.js.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.js +13 -16
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-suspended-display.test.js +10 -22
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
- package/dist/__tests__/thread-test-helpers.d.ts +7 -0
- package/dist/__tests__/thread-test-helpers.d.ts.map +1 -1
- package/dist/__tests__/thread-test-helpers.js +13 -0
- package/dist/__tests__/thread-test-helpers.js.map +1 -1
- package/dist/__tests__/thread.test.js +15 -13
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +105 -23
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
- package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-list-recursive.test.js +286 -0
- package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +46 -28
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-show-resolution.test.js +213 -0
- package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
- package/dist/__tests__/workflow-validate.test.d.ts +2 -0
- package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-validate.test.js +707 -0
- package/dist/__tests__/workflow-validate.test.js.map +1 -0
- package/dist/__tests__/write-envelope.test.d.ts +2 -0
- package/dist/__tests__/write-envelope.test.d.ts.map +1 -0
- package/dist/__tests__/write-envelope.test.js +201 -0
- package/dist/__tests__/write-envelope.test.js.map +1 -0
- package/dist/background/background.d.ts +22 -1
- package/dist/background/background.d.ts.map +1 -1
- package/dist/background/background.js +83 -6
- package/dist/background/background.js.map +1 -1
- package/dist/background/index.d.ts +1 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/types.d.ts +1 -0
- package/dist/background/types.d.ts.map +1 -1
- package/dist/cli.js +120 -62
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +3 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +17 -31
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +57 -31
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +12 -39
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +72 -303
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/step.d.ts +44 -1
- package/dist/commands/step.d.ts.map +1 -1
- package/dist/commands/step.js +255 -11
- package/dist/commands/step.js.map +1 -1
- package/dist/commands/thread.d.ts +16 -3
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +423 -142
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +9 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +126 -6
- package/dist/commands/workflow.js.map +1 -1
- package/dist/concurrency/concurrency.d.ts +34 -0
- package/dist/concurrency/concurrency.d.ts.map +1 -0
- package/dist/concurrency/concurrency.js +216 -0
- package/dist/concurrency/concurrency.js.map +1 -0
- package/dist/concurrency/index.d.ts +3 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +2 -0
- package/dist/concurrency/index.js.map +1 -0
- package/dist/concurrency/types.d.ts +19 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +2 -0
- package/dist/concurrency/types.js.map +1 -0
- package/dist/format.d.ts +69 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +198 -1
- package/dist/format.js.map +1 -1
- package/dist/moderator/__tests__/evaluate.test.js +31 -17
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
- package/dist/moderator/evaluate.d.ts.map +1 -1
- package/dist/moderator/evaluate.js +4 -16
- package/dist/moderator/evaluate.js.map +1 -1
- package/dist/moderator/index.d.ts +1 -2
- package/dist/moderator/index.d.ts.map +1 -1
- package/dist/moderator/index.js +0 -1
- package/dist/moderator/index.js.map +1 -1
- package/dist/moderator/types.d.ts +6 -10
- package/dist/moderator/types.d.ts.map +1 -1
- package/dist/moderator/types.js +1 -3
- package/dist/moderator/types.js.map +1 -1
- package/dist/output-mappers.d.ts +122 -0
- package/dist/output-mappers.d.ts.map +1 -0
- package/dist/output-mappers.js +134 -0
- package/dist/output-mappers.js.map +1 -0
- package/dist/schemas.d.ts +6 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +34 -5
- package/dist/schemas.js.map +1 -1
- package/dist/store.d.ts +28 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +75 -16
- package/dist/store.js.map +1 -1
- package/dist/text-renderers.d.ts +30 -0
- package/dist/text-renderers.d.ts.map +1 -0
- package/dist/text-renderers.js +251 -0
- package/dist/text-renderers.js.map +1 -0
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +95 -61
- package/dist/validate-semantic.js.map +1 -1
- package/dist/validate.d.ts +6 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +24 -0
- package/dist/validate.js.map +1 -1
- package/examples/brainstorm.yaml +130 -0
- package/examples/debate.yaml +169 -0
- package/examples/socratic-questioning.yaml +112 -0
- package/package.json +9 -10
- package/src/__tests__/adapter-json-roundtrip.test.ts +16 -7
- package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
- package/src/__tests__/build-step-entry.test.ts +203 -0
- package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
- package/src/__tests__/concurrency.test.ts +266 -0
- package/src/__tests__/config.test.ts +33 -321
- package/src/__tests__/current-role.test.ts +7 -6
- package/src/__tests__/e2e-mock-agent.test.ts +65 -30
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
- package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
- package/src/__tests__/format-text-default.test.ts +49 -0
- package/src/__tests__/format-text-registry.test.ts +173 -0
- package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
- package/src/__tests__/log-text-renderer.test.ts +294 -0
- package/src/__tests__/moderator-evaluate.test.ts +9 -52
- package/src/__tests__/output-mapper-thread-list-startedat.test.ts +124 -0
- package/src/__tests__/output-mapper-workflow-add.test.ts +24 -0
- package/src/__tests__/pid-recycling.test.ts +329 -0
- package/src/__tests__/prompt.test.ts +443 -2
- package/src/__tests__/resolve-head-hash.test.ts +11 -4
- package/src/__tests__/setup-agent-discovery.test.ts +26 -51
- package/src/__tests__/setup-complexity.test.ts +1 -203
- package/src/__tests__/setup-no-llm.test.ts +68 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +27 -31
- package/src/__tests__/step-ask.test.ts +677 -0
- package/src/__tests__/step-show-json.test.ts +1 -0
- package/src/__tests__/step-timing.test.ts +2 -0
- package/src/__tests__/store-global-cas.test.ts +2 -2
- package/src/__tests__/store-unified-threads.test.ts +30 -27
- package/src/__tests__/thread-cancel-status.test.ts +27 -20
- package/src/__tests__/thread-cancel-text-renderer.test.ts +125 -0
- package/src/__tests__/thread-list-filters.test.ts +443 -17
- package/src/__tests__/thread-list-template-ms-date.test.ts +110 -0
- package/src/__tests__/thread-list-workflow-corrupt.test.ts +198 -0
- package/src/__tests__/thread-poke.test.ts +554 -0
- package/src/__tests__/thread-read-xml-tags.test.ts +9 -11
- package/src/__tests__/thread-resume.test.ts +20 -15
- package/src/__tests__/thread-show-status.test.ts +17 -29
- package/src/__tests__/thread-start-cwd-cli.test.ts +15 -3
- package/src/__tests__/thread-stop-text-renderer.test.ts +168 -0
- package/src/__tests__/thread-suspend-step.test.ts +13 -16
- package/src/__tests__/thread-suspended-display.test.ts +10 -22
- package/src/__tests__/thread-test-helpers.ts +15 -1
- package/src/__tests__/thread.test.ts +14 -14
- package/src/__tests__/validate-semantic.test.ts +118 -33
- package/src/__tests__/workflow-list-recursive.test.ts +370 -0
- package/src/__tests__/workflow-resolution.test.ts +48 -29
- package/src/__tests__/workflow-show-resolution.test.ts +286 -0
- package/src/__tests__/workflow-validate.test.ts +828 -0
- package/src/__tests__/write-envelope.test.ts +257 -0
- package/src/background/background.ts +88 -6
- package/src/background/index.ts +2 -0
- package/src/background/types.ts +1 -0
- package/src/cli.ts +184 -77
- package/src/commands/config.ts +16 -33
- package/src/commands/prompt.ts +57 -31
- package/src/commands/setup.ts +80 -358
- package/src/commands/step.ts +339 -12
- package/src/commands/thread.ts +511 -171
- package/src/commands/workflow.ts +155 -4
- package/src/concurrency/concurrency.ts +245 -0
- package/src/concurrency/index.ts +10 -0
- package/src/concurrency/types.ts +19 -0
- package/src/format.ts +282 -2
- package/src/moderator/__tests__/evaluate.test.ts +34 -17
- package/src/moderator/evaluate.ts +5 -17
- package/src/moderator/index.ts +1 -6
- package/src/moderator/types.ts +6 -14
- package/src/output-mappers.ts +254 -0
- package/src/schemas.ts +51 -5
- package/src/store.ts +86 -20
- package/src/text-renderers.ts +355 -0
- package/src/validate-semantic.ts +125 -73
- package/src/validate.ts +27 -0
- package/dist/__tests__/setup-validate.test.d.ts +0 -2
- package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
- package/dist/__tests__/setup-validate.test.js +0 -108
- package/dist/__tests__/setup-validate.test.js.map +0 -1
- package/src/__tests__/setup-validate.test.ts +0 -148
- /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { formatOutput, getTextRenderer, TEXT_RENDERERS } from "../format.js";
|
|
3
|
+
import { renderLogList, renderLogShow } from "../text-renderers.js";
|
|
4
|
+
|
|
5
|
+
describe("log list — text renderer registration", () => {
|
|
6
|
+
test("TEXT_RENDERERS contains 'log list'", () => {
|
|
7
|
+
expect(getTextRenderer("log list")).toBeDefined();
|
|
8
|
+
expect(typeof getTextRenderer("log list")).toBe("function");
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
test("TEXT_RENDERERS['log list'] is the same reference as renderLogList", () => {
|
|
12
|
+
expect(TEXT_RENDERERS["log list"]).toBe(renderLogList);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("renderLogList is exported from text-renderers.ts", () => {
|
|
16
|
+
expect(typeof renderLogList).toBe("function");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("log show — text renderer registration", () => {
|
|
21
|
+
test("TEXT_RENDERERS contains 'log show'", () => {
|
|
22
|
+
expect(getTextRenderer("log show")).toBeDefined();
|
|
23
|
+
expect(typeof getTextRenderer("log show")).toBe("function");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("TEXT_RENDERERS['log show'] is the same reference as renderLogShow", () => {
|
|
27
|
+
expect(TEXT_RENDERERS["log show"]).toBe(renderLogShow);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("renderLogShow is exported from text-renderers.ts", () => {
|
|
31
|
+
expect(typeof renderLogShow).toBe("function");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("renderLogList — output shape", () => {
|
|
36
|
+
test("returns a string for full payload", () => {
|
|
37
|
+
const out = renderLogList([
|
|
38
|
+
{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" },
|
|
39
|
+
{ name: "2026-06-11.jsonl", size: 8192, date: "2026-06-11" },
|
|
40
|
+
]);
|
|
41
|
+
expect(typeof out).toBe("string");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("includes file names, dates, and sizes for each row", () => {
|
|
45
|
+
const out = renderLogList([
|
|
46
|
+
{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" },
|
|
47
|
+
{ name: "2026-06-11.jsonl", size: 8192, date: "2026-06-11" },
|
|
48
|
+
]);
|
|
49
|
+
expect(out).toContain("2026-06-10.jsonl");
|
|
50
|
+
expect(out).toContain("2026-06-10");
|
|
51
|
+
expect(out).toContain("2026-06-11.jsonl");
|
|
52
|
+
expect(out).toContain("2026-06-11");
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("includes a header line", () => {
|
|
56
|
+
const out = renderLogList([{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" }]);
|
|
57
|
+
const lines = out.split("\n");
|
|
58
|
+
expect(lines.length).toBeGreaterThanOrEqual(2);
|
|
59
|
+
// header should mention NAME or DATE or SIZE
|
|
60
|
+
const header = lines[0]?.toUpperCase() ?? "";
|
|
61
|
+
const hasHeader = header.includes("NAME") || header.includes("DATE") || header.includes("SIZE");
|
|
62
|
+
expect(hasHeader).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("does NOT begin with '{' or '[' (not raw JSON)", () => {
|
|
66
|
+
const out = renderLogList([{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" }]);
|
|
67
|
+
const trimmed = out.trimStart();
|
|
68
|
+
expect(trimmed.startsWith("{")).toBe(false);
|
|
69
|
+
expect(trimmed.startsWith("[")).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("does NOT contain literal 'undefined'", () => {
|
|
73
|
+
const out = renderLogList([{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" }]);
|
|
74
|
+
expect(out).not.toContain("undefined");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("empty array — returns string, no 'undefined'", () => {
|
|
78
|
+
const out = renderLogList([]);
|
|
79
|
+
expect(typeof out).toBe("string");
|
|
80
|
+
expect(out).not.toContain("undefined");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("null payload — returns string, does not throw", () => {
|
|
84
|
+
expect(() => renderLogList(null)).not.toThrow();
|
|
85
|
+
const out = renderLogList(null);
|
|
86
|
+
expect(typeof out).toBe("string");
|
|
87
|
+
expect(out).not.toContain("undefined");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("non-array payload — returns string, does not throw", () => {
|
|
91
|
+
expect(() => renderLogList({ name: "x" })).not.toThrow();
|
|
92
|
+
const out = renderLogList({ name: "x" });
|
|
93
|
+
expect(typeof out).toBe("string");
|
|
94
|
+
expect(out).not.toContain("undefined");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("partial item (missing size) — returns string, no 'undefined'", () => {
|
|
98
|
+
const out = renderLogList([{ name: "2026-06-10.jsonl", date: "2026-06-10" }]);
|
|
99
|
+
expect(typeof out).toBe("string");
|
|
100
|
+
expect(out).not.toContain("undefined");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("renderLogShow — output shape", () => {
|
|
105
|
+
test("returns a string for full payload", () => {
|
|
106
|
+
const out = renderLogShow([
|
|
107
|
+
{
|
|
108
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
109
|
+
pid: "12345",
|
|
110
|
+
tag: "4KNMR2PX",
|
|
111
|
+
msg: "Loading workflow...",
|
|
112
|
+
thread: "01JTEST000000000000THREAD1",
|
|
113
|
+
workflow: "WF1234567890A",
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
116
|
+
expect(typeof out).toBe("string");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("includes ts, pid, tag, and msg for each entry", () => {
|
|
120
|
+
const out = renderLogShow([
|
|
121
|
+
{
|
|
122
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
123
|
+
pid: "12345",
|
|
124
|
+
tag: "4KNMR2PX",
|
|
125
|
+
msg: "Loading workflow...",
|
|
126
|
+
thread: null,
|
|
127
|
+
workflow: null,
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
expect(out).toContain("2026-06-12T10:00:00.000Z");
|
|
131
|
+
expect(out).toContain("12345");
|
|
132
|
+
expect(out).toContain("4KNMR2PX");
|
|
133
|
+
expect(out).toContain("Loading workflow...");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("includes thread id when present", () => {
|
|
137
|
+
const out = renderLogShow([
|
|
138
|
+
{
|
|
139
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
140
|
+
pid: "12345",
|
|
141
|
+
tag: "4KNMR2PX",
|
|
142
|
+
msg: "Loading workflow...",
|
|
143
|
+
thread: "01JTEST000000000000THREAD1",
|
|
144
|
+
workflow: null,
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
expect(out).toContain("01JTEST000000000000THREAD1");
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("does NOT begin with '{' or '[' (not raw JSON)", () => {
|
|
151
|
+
const out = renderLogShow([
|
|
152
|
+
{
|
|
153
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
154
|
+
pid: "12345",
|
|
155
|
+
tag: "4KNMR2PX",
|
|
156
|
+
msg: "Loading workflow...",
|
|
157
|
+
thread: null,
|
|
158
|
+
workflow: null,
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
const trimmed = out.trimStart();
|
|
162
|
+
expect(trimmed.startsWith("{")).toBe(false);
|
|
163
|
+
expect(trimmed.startsWith("[")).toBe(false);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("does NOT contain literal 'undefined'", () => {
|
|
167
|
+
const out = renderLogShow([
|
|
168
|
+
{
|
|
169
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
170
|
+
pid: "12345",
|
|
171
|
+
tag: "4KNMR2PX",
|
|
172
|
+
msg: "Loading workflow...",
|
|
173
|
+
thread: null,
|
|
174
|
+
workflow: null,
|
|
175
|
+
},
|
|
176
|
+
]);
|
|
177
|
+
expect(out).not.toContain("undefined");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("empty array — returns string, no 'undefined'", () => {
|
|
181
|
+
const out = renderLogShow([]);
|
|
182
|
+
expect(typeof out).toBe("string");
|
|
183
|
+
expect(out).not.toContain("undefined");
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("null payload — returns string, does not throw", () => {
|
|
187
|
+
expect(() => renderLogShow(null)).not.toThrow();
|
|
188
|
+
const out = renderLogShow(null);
|
|
189
|
+
expect(typeof out).toBe("string");
|
|
190
|
+
expect(out).not.toContain("undefined");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("non-array payload — returns string, does not throw", () => {
|
|
194
|
+
expect(() => renderLogShow({ ts: "x" })).not.toThrow();
|
|
195
|
+
const out = renderLogShow({ ts: "x" });
|
|
196
|
+
expect(typeof out).toBe("string");
|
|
197
|
+
expect(out).not.toContain("undefined");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("partial entry (missing thread) — returns string, no 'undefined'", () => {
|
|
201
|
+
const out = renderLogShow([
|
|
202
|
+
{
|
|
203
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
204
|
+
pid: "12345",
|
|
205
|
+
tag: "4KNMR2PX",
|
|
206
|
+
msg: "hello",
|
|
207
|
+
},
|
|
208
|
+
]);
|
|
209
|
+
expect(typeof out).toBe("string");
|
|
210
|
+
expect(out).not.toContain("undefined");
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe("formatOutput integration — log list", () => {
|
|
215
|
+
test("formatOutput(data, 'text', 'log list') uses renderer", () => {
|
|
216
|
+
const data = [{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" }];
|
|
217
|
+
const out = formatOutput(data, "text", "log list");
|
|
218
|
+
expect(typeof out).toBe("string");
|
|
219
|
+
expect(out).not.toContain("undefined");
|
|
220
|
+
expect(out.trimStart().startsWith("{")).toBe(false);
|
|
221
|
+
expect(out.trimStart().startsWith("[")).toBe(false);
|
|
222
|
+
expect(out).toContain("2026-06-10.jsonl");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test("formatOutput(data, 'json', 'log list') still emits parseable JSON", () => {
|
|
226
|
+
const data = [{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" }];
|
|
227
|
+
const out = formatOutput(data, "json", "log list");
|
|
228
|
+
const parsed = JSON.parse(out);
|
|
229
|
+
expect(parsed).toEqual(data);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("formatOutput(data, 'yaml', 'log list') still emits YAML", () => {
|
|
233
|
+
const data = [{ name: "2026-06-10.jsonl", size: 4096, date: "2026-06-10" }];
|
|
234
|
+
const out = formatOutput(data, "yaml", "log list");
|
|
235
|
+
expect(typeof out).toBe("string");
|
|
236
|
+
expect(out).toContain("name:");
|
|
237
|
+
expect(out).toContain("2026-06-10.jsonl");
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
describe("formatOutput integration — log show", () => {
|
|
242
|
+
test("formatOutput(data, 'text', 'log show') uses renderer", () => {
|
|
243
|
+
const data = [
|
|
244
|
+
{
|
|
245
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
246
|
+
pid: "12345",
|
|
247
|
+
tag: "4KNMR2PX",
|
|
248
|
+
msg: "hello",
|
|
249
|
+
thread: null,
|
|
250
|
+
workflow: null,
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
const out = formatOutput(data, "text", "log show");
|
|
254
|
+
expect(typeof out).toBe("string");
|
|
255
|
+
expect(out).not.toContain("undefined");
|
|
256
|
+
expect(out.trimStart().startsWith("{")).toBe(false);
|
|
257
|
+
expect(out.trimStart().startsWith("[")).toBe(false);
|
|
258
|
+
expect(out).toContain("4KNMR2PX");
|
|
259
|
+
expect(out).toContain("hello");
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("formatOutput(data, 'json', 'log show') still emits parseable JSON", () => {
|
|
263
|
+
const data = [
|
|
264
|
+
{
|
|
265
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
266
|
+
pid: "12345",
|
|
267
|
+
tag: "4KNMR2PX",
|
|
268
|
+
msg: "hello",
|
|
269
|
+
thread: null,
|
|
270
|
+
workflow: null,
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
const out = formatOutput(data, "json", "log show");
|
|
274
|
+
const parsed = JSON.parse(out);
|
|
275
|
+
expect(parsed).toEqual(data);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
test("formatOutput(data, 'yaml', 'log show') still emits YAML", () => {
|
|
279
|
+
const data = [
|
|
280
|
+
{
|
|
281
|
+
ts: "2026-06-12T10:00:00.000Z",
|
|
282
|
+
pid: "12345",
|
|
283
|
+
tag: "4KNMR2PX",
|
|
284
|
+
msg: "hello",
|
|
285
|
+
thread: null,
|
|
286
|
+
workflow: null,
|
|
287
|
+
},
|
|
288
|
+
];
|
|
289
|
+
const out = formatOutput(data, "yaml", "log show");
|
|
290
|
+
expect(typeof out).toBe("string");
|
|
291
|
+
expect(out).toContain("ts:");
|
|
292
|
+
expect(out).toContain("pid:");
|
|
293
|
+
});
|
|
294
|
+
});
|
|
@@ -13,14 +13,14 @@ const solveIssueGraph: WorkflowPayload["graph"] = {
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
planner: {
|
|
16
|
-
planned: { role: "developer", prompt: "Implement the plan: {{plan}}", location: null },
|
|
16
|
+
planned: { role: "developer", prompt: "Implement the plan: {{ plan }}", location: null },
|
|
17
17
|
},
|
|
18
18
|
developer: {
|
|
19
|
-
implemented: { role: "reviewer", prompt: "Review the changes: {{summary}}", location: null },
|
|
19
|
+
implemented: { role: "reviewer", prompt: "Review the changes: {{ summary }}", location: null },
|
|
20
20
|
},
|
|
21
21
|
reviewer: {
|
|
22
22
|
approved: { role: "$END", prompt: "Done.", location: null },
|
|
23
|
-
rejected: { role: "developer", prompt: "Fix: {{comments}}", location: null },
|
|
23
|
+
rejected: { role: "developer", prompt: "Fix: {{ comments }}", location: null },
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -68,49 +68,6 @@ describe("evaluate", () => {
|
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
test("status-based routing (needs input → $SUSPEND)", () => {
|
|
72
|
-
const graph: Record<string, Record<string, Target>> = {
|
|
73
|
-
...solveIssueGraph,
|
|
74
|
-
reviewer: {
|
|
75
|
-
...solveIssueGraph.reviewer,
|
|
76
|
-
needs_input: { role: "$SUSPEND", prompt: "Waiting for user input.", location: null },
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
const result = evaluate(graph, "reviewer", { $status: "needs_input" });
|
|
80
|
-
expect(result).toEqual({
|
|
81
|
-
ok: true,
|
|
82
|
-
value: {
|
|
83
|
-
action: "suspend",
|
|
84
|
-
suspendedRole: "reviewer",
|
|
85
|
-
prompt: "Waiting for user input.",
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("$SUSPEND prompt template renders mustache variables", () => {
|
|
91
|
-
const graph: Record<string, Record<string, Target>> = {
|
|
92
|
-
reviewer: {
|
|
93
|
-
needs_input: {
|
|
94
|
-
role: "$SUSPEND",
|
|
95
|
-
prompt: "Please clarify: {{{question}}}",
|
|
96
|
-
location: null,
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
const result = evaluate(graph, "reviewer", {
|
|
101
|
-
$status: "needs_input",
|
|
102
|
-
question: "Which API endpoint?",
|
|
103
|
-
});
|
|
104
|
-
expect(result).toEqual({
|
|
105
|
-
ok: true,
|
|
106
|
-
value: {
|
|
107
|
-
action: "suspend",
|
|
108
|
-
suspendedRole: "reviewer",
|
|
109
|
-
prompt: "Please clarify: Which API endpoint?",
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
71
|
test("missing role in graph → error", () => {
|
|
115
72
|
const result = evaluate(solveIssueGraph, "unknown-role", { $status: "new" });
|
|
116
73
|
expect(result.ok).toBe(false);
|
|
@@ -127,7 +84,7 @@ describe("evaluate", () => {
|
|
|
127
84
|
}
|
|
128
85
|
});
|
|
129
86
|
|
|
130
|
-
test("
|
|
87
|
+
test("liquid template rendering with simple fields", () => {
|
|
131
88
|
const result = evaluate(solveIssueGraph, "planner", {
|
|
132
89
|
$status: "planned",
|
|
133
90
|
plan: "Add auth middleware",
|
|
@@ -142,7 +99,7 @@ describe("evaluate", () => {
|
|
|
142
99
|
});
|
|
143
100
|
});
|
|
144
101
|
|
|
145
|
-
test("
|
|
102
|
+
test("liquid does not HTML-escape prompt content", () => {
|
|
146
103
|
const result = evaluate(solveIssueGraph, "reviewer", {
|
|
147
104
|
$status: "rejected",
|
|
148
105
|
comments: 'use <T> & "Result<T, E>" types',
|
|
@@ -153,10 +110,10 @@ describe("evaluate", () => {
|
|
|
153
110
|
});
|
|
154
111
|
});
|
|
155
112
|
|
|
156
|
-
test("
|
|
113
|
+
test("liquid renders HTML content without escaping", () => {
|
|
157
114
|
const graph: Record<string, Record<string, Target>> = {
|
|
158
115
|
reviewer: {
|
|
159
|
-
rejected: { role: "developer", prompt: "Fix: {{
|
|
116
|
+
rejected: { role: "developer", prompt: "Fix: {{ comments }}", location: null },
|
|
160
117
|
},
|
|
161
118
|
};
|
|
162
119
|
const result = evaluate(graph, "reviewer", {
|
|
@@ -181,12 +138,12 @@ describe("evaluate", () => {
|
|
|
181
138
|
}
|
|
182
139
|
});
|
|
183
140
|
|
|
184
|
-
test("
|
|
141
|
+
test("liquid template with nested object paths", () => {
|
|
185
142
|
const graph: Record<string, Record<string, Target>> = {
|
|
186
143
|
reviewer: {
|
|
187
144
|
rejected: {
|
|
188
145
|
role: "developer",
|
|
189
|
-
prompt: "Address: {{review.comments}}",
|
|
146
|
+
prompt: "Address: {{ review.comments }}",
|
|
190
147
|
location: null,
|
|
191
148
|
},
|
|
192
149
|
},
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import type { CasRef, ThreadId } from "@united-workforce/protocol";
|
|
4
|
+
import { generateUlid } from "@united-workforce/util";
|
|
5
|
+
import { describe, expect, test } from "vitest";
|
|
6
|
+
import type { ThreadListItemWithStatus } from "../commands/thread.js";
|
|
7
|
+
import { toThreadListPayload } from "../output-mappers.js";
|
|
8
|
+
|
|
9
|
+
const OUTPUT_MAPPERS_PATH = fileURLToPath(new URL("../output-mappers.ts", import.meta.url));
|
|
10
|
+
|
|
11
|
+
function makeItem(threadId: string): ThreadListItemWithStatus {
|
|
12
|
+
return {
|
|
13
|
+
thread: threadId as ThreadId,
|
|
14
|
+
workflow: "WORKFLOWHASH1" as CasRef,
|
|
15
|
+
head: "HEADHASH00001" as CasRef,
|
|
16
|
+
status: "idle",
|
|
17
|
+
currentRole: null,
|
|
18
|
+
statusDisplay: "idle",
|
|
19
|
+
workflowName: "test-workflow",
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe("toThreadListPayload — issue #343 (ULID timestamp decoded with padding stripped)", () => {
|
|
24
|
+
test("startedAt equals the millisecond timestamp originally passed to generateUlid", () => {
|
|
25
|
+
const ts = 1781219097830; // 2026-06-11T23:04:57.830Z
|
|
26
|
+
const ulid = generateUlid(ts);
|
|
27
|
+
|
|
28
|
+
const payload = toThreadListPayload([makeItem(ulid)]);
|
|
29
|
+
|
|
30
|
+
expect(payload.items).toHaveLength(1);
|
|
31
|
+
expect(payload.items[0]!.startedAt).toBe(ts);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("startedAt is NOT the raw 50-bit value (i.e. NOT timestamp << 2)", () => {
|
|
35
|
+
const ts = 1781219097830;
|
|
36
|
+
const ulid = generateUlid(ts);
|
|
37
|
+
|
|
38
|
+
const payload = toThreadListPayload([makeItem(ulid)]);
|
|
39
|
+
|
|
40
|
+
// The buggy decoder produced ts * 4 = 7124876391323, pushing year to 2195.
|
|
41
|
+
expect(payload.items[0]!.startedAt).not.toBe(ts * 4);
|
|
42
|
+
expect(payload.items[0]!.startedAt).not.toBe(7124876391323);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("startedAt decodes to year 2026 for the issue-reported ULID timestamp", () => {
|
|
46
|
+
const ts = 1781219097830;
|
|
47
|
+
const ulid = generateUlid(ts);
|
|
48
|
+
|
|
49
|
+
const payload = toThreadListPayload([makeItem(ulid)]);
|
|
50
|
+
|
|
51
|
+
const startedAt = payload.items[0]!.startedAt;
|
|
52
|
+
expect(startedAt).not.toBeNull();
|
|
53
|
+
if (startedAt === null) return;
|
|
54
|
+
const isoDate = new Date(startedAt).toISOString().slice(0, 10);
|
|
55
|
+
expect(isoDate).toBe("2026-06-11");
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("round-trips correctly across several timestamps", () => {
|
|
59
|
+
const timestamps = [
|
|
60
|
+
0,
|
|
61
|
+
Date.UTC(2020, 0, 1, 0, 0, 0),
|
|
62
|
+
Date.UTC(2023, 5, 15, 12, 30, 45),
|
|
63
|
+
Date.UTC(2026, 4, 20, 0, 0, 0),
|
|
64
|
+
Date.UTC(2030, 11, 31, 23, 59, 59),
|
|
65
|
+
];
|
|
66
|
+
const items = timestamps.map((t) => makeItem(generateUlid(t)));
|
|
67
|
+
|
|
68
|
+
const payload = toThreadListPayload(items);
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < timestamps.length; i++) {
|
|
71
|
+
expect(payload.items[i]!.startedAt).toBe(timestamps[i]);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("startedAt is null for thread ids that are not valid 26-char Crockford Base32 ULIDs", () => {
|
|
76
|
+
const cases = ["", "TOOSHORT", "TOOLONGAAAAAAAAAAAAAAAAAA", "INVALID!@#$%^&CHARACTERS"];
|
|
77
|
+
const items = cases.map((id) => makeItem(id));
|
|
78
|
+
|
|
79
|
+
const payload = toThreadListPayload(items);
|
|
80
|
+
|
|
81
|
+
for (const item of payload.items) {
|
|
82
|
+
expect(item.startedAt).toBeNull();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("preserves other thread-list item fields verbatim", () => {
|
|
87
|
+
const ts = 1781219097830;
|
|
88
|
+
const ulid = generateUlid(ts);
|
|
89
|
+
const item: ThreadListItemWithStatus = {
|
|
90
|
+
thread: ulid as ThreadId,
|
|
91
|
+
workflow: "WORKFLOWHASH9" as CasRef,
|
|
92
|
+
head: "HEADHASH99999" as CasRef,
|
|
93
|
+
status: "running",
|
|
94
|
+
currentRole: "developer",
|
|
95
|
+
statusDisplay: "running",
|
|
96
|
+
workflowName: "solve-issue",
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const payload = toThreadListPayload([item]);
|
|
100
|
+
|
|
101
|
+
expect(payload.items[0]).toEqual({
|
|
102
|
+
threadId: ulid,
|
|
103
|
+
workflowHash: "WORKFLOWHASH9",
|
|
104
|
+
workflowName: "solve-issue",
|
|
105
|
+
status: "running",
|
|
106
|
+
currentRole: "developer",
|
|
107
|
+
startedAt: ts,
|
|
108
|
+
completedAt: null,
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe("output-mappers.ts source — issue #343 refactor", () => {
|
|
114
|
+
test("does NOT contain a local extractUlidTime function (removed in favor of util)", async () => {
|
|
115
|
+
const source = await readFile(OUTPUT_MAPPERS_PATH, "utf8");
|
|
116
|
+
expect(source).not.toMatch(/function\s+extractUlidTime\b/);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("imports extractUlidTimestamp from @united-workforce/util", async () => {
|
|
120
|
+
const source = await readFile(OUTPUT_MAPPERS_PATH, "utf8");
|
|
121
|
+
expect(source).toMatch(/extractUlidTimestamp/);
|
|
122
|
+
expect(source).toMatch(/@united-workforce\/util/);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { toWorkflowAddPayload, type WorkflowAddPayload } from "../output-mappers.js";
|
|
3
|
+
|
|
4
|
+
describe("toWorkflowAddPayload — issue #334", () => {
|
|
5
|
+
test("maps WorkflowAddOutput { name, hash } to plain payload shape", () => {
|
|
6
|
+
const out = toWorkflowAddPayload({ name: "review-pr", hash: "2TBP6T37TZAJZ" });
|
|
7
|
+
expect(out).toEqual({ name: "review-pr", hash: "2TBP6T37TZAJZ" });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("returns a WorkflowAddPayload type with exactly two fields", () => {
|
|
11
|
+
const out: WorkflowAddPayload = toWorkflowAddPayload({
|
|
12
|
+
name: "solve-issue",
|
|
13
|
+
hash: "76C98RVXA5E4F",
|
|
14
|
+
});
|
|
15
|
+
expect(Object.keys(out).sort()).toEqual(["hash", "name"]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("performs no I/O — pure data mapping", () => {
|
|
19
|
+
// Repeated calls produce equal results
|
|
20
|
+
const a = toWorkflowAddPayload({ name: "a", hash: "AAA1234567890" });
|
|
21
|
+
const b = toWorkflowAddPayload({ name: "a", hash: "AAA1234567890" });
|
|
22
|
+
expect(a).toEqual(b);
|
|
23
|
+
});
|
|
24
|
+
});
|