@minicor/mcp-server 4.10.0 → 4.12.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.
@@ -53,7 +53,7 @@ function routedFetch(routes) {
53
53
  /** A GET /blueprints/:id response (Blueprint & BlueprintStateInfo). */
54
54
  function bpFixture(overrides = {}) {
55
55
  return {
56
- id: "bp-1",
56
+ id: "5668a1ae-3912-4713-b269-f98817b9ccd9",
57
57
  workspaceId: 11,
58
58
  name: "COI intake",
59
59
  document: { goal: "", steps: [], openQuestions: [] },
@@ -87,6 +87,73 @@ describe("blueprint tools", () => {
87
87
  process.env.MIDDLEWARE_SERVICE_URL = savedMwUrl;
88
88
  vi.unstubAllGlobals();
89
89
  });
90
+ // ── uuid validation (F2: hallucinated ids reached prod as 500s) ──
91
+ it("rejects an integer blueprintId client-side without calling the service (ws 264 incident shape)", async () => {
92
+ const fetchMock = vi.fn();
93
+ vi.stubGlobal("fetch", fetchMock);
94
+ const { client, embedded } = await startServer();
95
+ try {
96
+ const result = await client.callTool({
97
+ name: "blueprint_get",
98
+ arguments: { workspaceId: 11, blueprintId: "75" },
99
+ });
100
+ expect(result.isError).toBe(true);
101
+ expect(result.content[0].text).toContain("blueprintId must be a uuid; call blueprint_list to get real ids");
102
+ expect(fetchMock).not.toHaveBeenCalled();
103
+ }
104
+ finally {
105
+ await client.close();
106
+ await embedded.close();
107
+ }
108
+ });
109
+ it("rejects a spliced non-uuid blueprintId (ws 277 incident shape)", async () => {
110
+ const fetchMock = vi.fn();
111
+ vi.stubGlobal("fetch", fetchMock);
112
+ const { client, embedded } = await startServer();
113
+ try {
114
+ const result = await client.callTool({
115
+ name: "blueprint_add_entry",
116
+ arguments: {
117
+ workspaceId: 11,
118
+ // Two real uuids mis-spliced at a shared hex token, verbatim from
119
+ // the prod incident. 36+ chars of valid hex, still not a uuid.
120
+ blueprintId: "be9b3716-a487-4eac-886d-e632faadfd2c01d9",
121
+ kind: "note",
122
+ title: "n",
123
+ text: "t",
124
+ },
125
+ });
126
+ expect(result.isError).toBe(true);
127
+ expect(result.content[0].text).toContain("blueprintId must be a uuid; call blueprint_list to get real ids");
128
+ expect(fetchMock).not.toHaveBeenCalled();
129
+ }
130
+ finally {
131
+ await client.close();
132
+ await embedded.close();
133
+ }
134
+ });
135
+ it("rejects a non-uuid entryId with a blueprint_list_entries hint", async () => {
136
+ const fetchMock = vi.fn();
137
+ vi.stubGlobal("fetch", fetchMock);
138
+ const { client, embedded } = await startServer();
139
+ try {
140
+ const result = await client.callTool({
141
+ name: "blueprint_list_entries",
142
+ arguments: {
143
+ workspaceId: 11,
144
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
145
+ entryId: "e1",
146
+ },
147
+ });
148
+ expect(result.isError).toBe(true);
149
+ expect(result.content[0].text).toContain("entryId must be a uuid; call blueprint_list_entries to get real ids");
150
+ expect(fetchMock).not.toHaveBeenCalled();
151
+ }
152
+ finally {
153
+ await client.close();
154
+ await embedded.close();
155
+ }
156
+ });
90
157
  // ── baseVersion CAS ────────────────────────────────────────
91
158
  it("blueprint_update surfaces the baseVersion CAS 409 as a structured version_conflict", async () => {
92
159
  const fetchMock = vi.fn(async (_input, _init) => jsonResponse({ message: "Blueprint has moved to version 7 (edit was based on 4)" }, 409));
@@ -97,13 +164,13 @@ describe("blueprint tools", () => {
97
164
  name: "blueprint_update",
98
165
  arguments: {
99
166
  workspaceId: 11,
100
- blueprintId: "bp-1",
167
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
101
168
  patch: { goal: "New goal" },
102
169
  baseVersion: 4,
103
170
  },
104
171
  });
105
172
  const [url, init] = fetchMock.mock.calls[0];
106
- expect(String(url)).toBe(`${MW_BASE}${BP}/bp-1`);
173
+ expect(String(url)).toBe(`${MW_BASE}${BP}/5668a1ae-3912-4713-b269-f98817b9ccd9`);
107
174
  expect(init.method).toBe("PATCH");
108
175
  expect(JSON.parse(String(init.body)).baseVersion).toBe(4);
109
176
  const payload = parseToolJson(result);
@@ -126,7 +193,7 @@ describe("blueprint tools", () => {
126
193
  name: "blueprint_update",
127
194
  arguments: {
128
195
  workspaceId: 11,
129
- blueprintId: "bp-1",
196
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
130
197
  patch: { goal: "New goal" },
131
198
  },
132
199
  });
@@ -151,7 +218,7 @@ describe("blueprint tools", () => {
151
218
  try {
152
219
  const result = await client.callTool({
153
220
  name: "blueprint_update",
154
- arguments: { workspaceId: 11, blueprintId: "bp-1", name: "Renamed" },
221
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9", name: "Renamed" },
155
222
  });
156
223
  const payload = parseToolJson(result);
157
224
  expect(payload.status).toBe("updated");
@@ -187,7 +254,7 @@ describe("blueprint tools", () => {
187
254
  try {
188
255
  const result = await client.callTool({
189
256
  name: "blueprint_synthesize",
190
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
257
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
191
258
  });
192
259
  const payload = parseToolJson(result);
193
260
  expect(payload.status).toBe("blocked");
@@ -216,7 +283,7 @@ describe("blueprint tools", () => {
216
283
  try {
217
284
  const result = await client.callTool({
218
285
  name: "blueprint_synthesize",
219
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
286
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
220
287
  });
221
288
  const payload = parseToolJson(result);
222
289
  expect(payload.status).toBe("blocked");
@@ -246,10 +313,10 @@ describe("blueprint tools", () => {
246
313
  try {
247
314
  const result = await client.callTool({
248
315
  name: "blueprint_synthesize",
249
- arguments: { workspaceId: 11, blueprintId: "bp-1", replace: true },
316
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9", replace: true },
250
317
  });
251
318
  const [url, init] = fetchMock.mock.calls[0];
252
- expect(String(url)).toBe(`${MW_BASE}${BP}/bp-1/synthesize`);
319
+ expect(String(url)).toBe(`${MW_BASE}${BP}/5668a1ae-3912-4713-b269-f98817b9ccd9/synthesize`);
253
320
  expect(JSON.parse(String(init.body))).toEqual({ replace: true });
254
321
  const payload = parseToolJson(result);
255
322
  expect(payload.status).toBe("proposal_ready");
@@ -263,7 +330,7 @@ describe("blueprint tools", () => {
263
330
  // ── apply-proposal swap guard ──────────────────────────────
264
331
  it("blueprint_apply_proposal refuses (without POSTing) when the pending proposal was swapped", async () => {
265
332
  const fetchMock = routedFetch({
266
- "/blueprints/bp-1": bpFixture({
333
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
267
334
  hasPendingProposal: true,
268
335
  pendingProposal: {
269
336
  patch: {},
@@ -282,7 +349,7 @@ describe("blueprint tools", () => {
282
349
  name: "blueprint_apply_proposal",
283
350
  arguments: {
284
351
  workspaceId: 11,
285
- blueprintId: "bp-1",
352
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
286
353
  baseVersion: 2,
287
354
  proposalBasedOnSeq: 3,
288
355
  },
@@ -309,7 +376,7 @@ describe("blueprint tools", () => {
309
376
  createdAt: "2026-08-12T00:00:00Z",
310
377
  };
311
378
  const fetchMock = routedFetch({
312
- "/blueprints/bp-1/proposal/apply": bpFixture({
379
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/proposal/apply": bpFixture({
313
380
  headVersion: 3,
314
381
  state: "job_stale",
315
382
  document: {
@@ -318,7 +385,7 @@ describe("blueprint tools", () => {
318
385
  openQuestions: [],
319
386
  },
320
387
  }),
321
- "/blueprints/bp-1": bpFixture({
388
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
322
389
  hasPendingProposal: true,
323
390
  pendingProposal,
324
391
  }),
@@ -330,7 +397,7 @@ describe("blueprint tools", () => {
330
397
  name: "blueprint_apply_proposal",
331
398
  arguments: {
332
399
  workspaceId: 11,
333
- blueprintId: "bp-1",
400
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
334
401
  baseVersion: 2,
335
402
  proposalBasedOnSeq: 3,
336
403
  },
@@ -380,7 +447,7 @@ describe("blueprint tools", () => {
380
447
  name: "blueprint_apply_proposal",
381
448
  arguments: {
382
449
  workspaceId: 11,
383
- blueprintId: "bp-1",
450
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
384
451
  baseVersion: 2,
385
452
  proposalBasedOnSeq: 3,
386
453
  },
@@ -421,7 +488,7 @@ describe("blueprint tools", () => {
421
488
  name: "blueprint_apply_proposal",
422
489
  arguments: {
423
490
  workspaceId: 11,
424
- blueprintId: "bp-1",
491
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
425
492
  baseVersion: 2,
426
493
  proposalBasedOnSeq: 3,
427
494
  },
@@ -442,7 +509,7 @@ describe("blueprint tools", () => {
442
509
  // re-read instead of reporting headVersion: undefined (the next CAS base).
443
510
  it("blueprint_update re-reads for headVersion when PATCH returns the raw row", async () => {
444
511
  const rawRow = {
445
- id: "bp-1",
512
+ id: "5668a1ae-3912-4713-b269-f98817b9ccd9",
446
513
  workspaceId: 11,
447
514
  name: "COI intake",
448
515
  document: { goal: "New goal", steps: [], openQuestions: [] },
@@ -459,7 +526,7 @@ describe("blueprint tools", () => {
459
526
  name: "blueprint_update",
460
527
  arguments: {
461
528
  workspaceId: 11,
462
- blueprintId: "bp-1",
529
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
463
530
  patch: { goal: "New goal" },
464
531
  baseVersion: 4,
465
532
  },
@@ -491,7 +558,7 @@ describe("blueprint tools", () => {
491
558
  applied = true;
492
559
  // Raw row: no headVersion, no state.
493
560
  return jsonResponse({
494
- id: "bp-1",
561
+ id: "5668a1ae-3912-4713-b269-f98817b9ccd9",
495
562
  workspaceId: 11,
496
563
  document: { goal: "Do the thing", steps: [{ id: "s1" }], openQuestions: [] },
497
564
  });
@@ -508,7 +575,7 @@ describe("blueprint tools", () => {
508
575
  name: "blueprint_apply_proposal",
509
576
  arguments: {
510
577
  workspaceId: 11,
511
- blueprintId: "bp-1",
578
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
512
579
  baseVersion: 2,
513
580
  proposalBasedOnSeq: 3,
514
581
  },
@@ -525,7 +592,7 @@ describe("blueprint tools", () => {
525
592
  });
526
593
  it("blueprint_update marks the response partial when the post-write re-read ALSO fails", async () => {
527
594
  const rawRow = {
528
- id: "bp-1",
595
+ id: "5668a1ae-3912-4713-b269-f98817b9ccd9",
529
596
  workspaceId: 11,
530
597
  name: "COI intake",
531
598
  document: { goal: "New goal", steps: [], openQuestions: [] },
@@ -542,7 +609,7 @@ describe("blueprint tools", () => {
542
609
  name: "blueprint_update",
543
610
  arguments: {
544
611
  workspaceId: 11,
545
- blueprintId: "bp-1",
612
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
546
613
  patch: { goal: "New goal" },
547
614
  baseVersion: 4,
548
615
  },
@@ -568,7 +635,7 @@ describe("blueprint tools", () => {
568
635
  try {
569
636
  const result = await client.callTool({
570
637
  name: "blueprint_reject_proposal",
571
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
638
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
572
639
  });
573
640
  const payload = parseToolJson(result);
574
641
  expect(payload.status).toBe("already_gone");
@@ -588,7 +655,7 @@ describe("blueprint tools", () => {
588
655
  name: "blueprint_answer_question",
589
656
  arguments: {
590
657
  workspaceId: 11,
591
- blueprintId: "bp-1",
658
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
592
659
  question: "Which account?",
593
660
  answer: "The ops account",
594
661
  },
@@ -603,7 +670,7 @@ describe("blueprint tools", () => {
603
670
  }
604
671
  });
605
672
  it("blueprint_answer_question propagates a blueprint-missing 404 instead of shaping it as convergence", async () => {
606
- const fetchMock = vi.fn(async (_input, _init) => jsonResponse({ message: "Blueprint bp-1 not found" }, 404));
673
+ const fetchMock = vi.fn(async (_input, _init) => jsonResponse({ message: "Blueprint 5668a1ae-3912-4713-b269-f98817b9ccd9 not found" }, 404));
607
674
  vi.stubGlobal("fetch", fetchMock);
608
675
  const { client, embedded } = await startServer();
609
676
  try {
@@ -611,7 +678,7 @@ describe("blueprint tools", () => {
611
678
  name: "blueprint_answer_question",
612
679
  arguments: {
613
680
  workspaceId: 11,
614
- blueprintId: "bp-1",
681
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
615
682
  question: "Which account?",
616
683
  answer: "The ops account",
617
684
  },
@@ -626,7 +693,7 @@ describe("blueprint tools", () => {
626
693
  }
627
694
  });
628
695
  it("blueprint_apply_proposal propagates a blueprint-missing 404 instead of shaping it as convergence", async () => {
629
- const fetchMock = vi.fn(async (_input, _init) => jsonResponse({ message: "Blueprint bp-1 not found" }, 404));
696
+ const fetchMock = vi.fn(async (_input, _init) => jsonResponse({ message: "Blueprint 5668a1ae-3912-4713-b269-f98817b9ccd9 not found" }, 404));
630
697
  vi.stubGlobal("fetch", fetchMock);
631
698
  const { client, embedded } = await startServer();
632
699
  try {
@@ -634,7 +701,7 @@ describe("blueprint tools", () => {
634
701
  name: "blueprint_apply_proposal",
635
702
  arguments: {
636
703
  workspaceId: 11,
637
- blueprintId: "bp-1",
704
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
638
705
  baseVersion: 1,
639
706
  proposalBasedOnSeq: 1,
640
707
  },
@@ -663,13 +730,13 @@ describe("blueprint tools", () => {
663
730
  name: "blueprint_answer_question",
664
731
  arguments: {
665
732
  workspaceId: 11,
666
- blueprintId: "bp-1",
733
+ blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9",
667
734
  question: "First question?",
668
735
  answer: "Answered",
669
736
  },
670
737
  });
671
738
  const [url, init] = fetchMock.mock.calls[0];
672
- expect(String(url)).toBe(`${MW_BASE}${BP}/bp-1/questions/answer`);
739
+ expect(String(url)).toBe(`${MW_BASE}${BP}/5668a1ae-3912-4713-b269-f98817b9ccd9/questions/answer`);
673
740
  expect(JSON.parse(String(init.body))).toEqual({
674
741
  question: "First question?",
675
742
  answer: "Answered",
@@ -687,7 +754,7 @@ describe("blueprint tools", () => {
687
754
  // ── build precondition + handoff artifact ──────────────────
688
755
  it("blueprint_build blocks (without syncing) when the linked build run is needs_input", async () => {
689
756
  const fetchMock = routedFetch({
690
- "/blueprints/bp-1": bpFixture({
757
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
691
758
  state: "building",
692
759
  buildStatus: "needs_input",
693
760
  middlewareId: "mw-1",
@@ -700,7 +767,7 @@ describe("blueprint tools", () => {
700
767
  try {
701
768
  const result = await client.callTool({
702
769
  name: "blueprint_build",
703
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
770
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
704
771
  });
705
772
  // Only the precondition GET — no POST /sync.
706
773
  expect(fetchMock).toHaveBeenCalledTimes(1);
@@ -716,13 +783,13 @@ describe("blueprint tools", () => {
716
783
  });
717
784
  it("blueprint_build returns the handoff triple and points at the jobs loop", async () => {
718
785
  const fetchMock = routedFetch({
719
- "/blueprints/bp-1/sync": {
786
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/sync": {
720
787
  middlewareId: "mw-1",
721
788
  routeId: "route-1",
722
789
  buildId: "b-2",
723
790
  testCaseIds: ["tc-1", "tc-2"],
724
791
  },
725
- "/blueprints/bp-1": bpFixture({
792
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
726
793
  state: "job_stale",
727
794
  document: { goal: "Do the thing", steps: [{ id: "s1" }] },
728
795
  headVersion: 2,
@@ -733,7 +800,7 @@ describe("blueprint tools", () => {
733
800
  try {
734
801
  const result = await client.callTool({
735
802
  name: "blueprint_build",
736
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
803
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
737
804
  });
738
805
  const payload = parseToolJson(result);
739
806
  expect(payload.status).toBe("build_queued");
@@ -766,7 +833,7 @@ describe("blueprint tools", () => {
766
833
  try {
767
834
  const result = await client.callTool({
768
835
  name: "blueprint_build_status",
769
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
836
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
770
837
  });
771
838
  const payload = parseToolJson(result);
772
839
  expect(payload.build).toBeNull();
@@ -787,7 +854,7 @@ describe("blueprint tools", () => {
787
854
  try {
788
855
  const result = await client.callTool({
789
856
  name: "resolve_blueprint_state",
790
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
857
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
791
858
  });
792
859
  return parseToolJson(result);
793
860
  }
@@ -809,7 +876,7 @@ describe("blueprint tools", () => {
809
876
  try {
810
877
  const result = await client.callTool({
811
878
  name: "resolve_blueprint_state",
812
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
879
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
813
880
  });
814
881
  const payload = parseToolJson(result);
815
882
  // The empty spec + unreadable library must NOT unfold to no_context
@@ -846,7 +913,7 @@ describe("blueprint tools", () => {
846
913
  try {
847
914
  const result = await client.callTool({
848
915
  name: "resolve_blueprint_state",
849
- arguments: { workspaceId: 11, blueprintId: "bp-1" },
916
+ arguments: { workspaceId: 11, blueprintId: "5668a1ae-3912-4713-b269-f98817b9ccd9" },
850
917
  });
851
918
  const payload = parseToolJson(result);
852
919
  // An unreadable linked build must NOT unfold to a normal lifecycle
@@ -864,18 +931,18 @@ describe("blueprint tools", () => {
864
931
  });
865
932
  it("empty blueprint -> no_context -> feed the library", async () => {
866
933
  const payload = await resolve({
867
- "/blueprints/bp-1/entries": [],
868
- "/blueprints/bp-1": bpFixture(),
934
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [],
935
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture(),
869
936
  });
870
937
  expect(payload.state).toBe("no_context");
871
938
  expect(payload.nextActions.join("\n")).toContain("blueprint_add_entry");
872
939
  });
873
940
  it("pending entries -> enriching -> poll the library", async () => {
874
941
  const payload = await resolve({
875
- "/blueprints/bp-1/entries": [
942
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
876
943
  { id: "e1", kind: "video", title: "Loom", status: "pending", seq: 1 },
877
944
  ],
878
- "/blueprints/bp-1": bpFixture({
945
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
879
946
  librarySeq: 1,
880
947
  specStale: true,
881
948
  state: "spec_stale",
@@ -887,10 +954,10 @@ describe("blueprint tools", () => {
887
954
  });
888
955
  it("ready library + empty spec -> no_spec -> synthesize", async () => {
889
956
  const payload = await resolve({
890
- "/blueprints/bp-1/entries": [
957
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
891
958
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
892
959
  ],
893
- "/blueprints/bp-1": bpFixture({
960
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
894
961
  librarySeq: 1,
895
962
  specStale: true,
896
963
  state: "spec_stale",
@@ -901,10 +968,10 @@ describe("blueprint tools", () => {
901
968
  });
902
969
  it("pending proposal -> proposal_pending with prefilled apply coordinates", async () => {
903
970
  const payload = await resolve({
904
- "/blueprints/bp-1/entries": [
971
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
905
972
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
906
973
  ],
907
- "/blueprints/bp-1": bpFixture({
974
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
908
975
  hasPendingProposal: true,
909
976
  pendingProposal: {
910
977
  patch: {},
@@ -924,10 +991,10 @@ describe("blueprint tools", () => {
924
991
  });
925
992
  it("open questions -> needs_answers with one answer call per question, then ONE synthesize", async () => {
926
993
  const payload = await resolve({
927
- "/blueprints/bp-1/entries": [
994
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
928
995
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
929
996
  ],
930
- "/blueprints/bp-1": bpFixture({
997
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
931
998
  document: {
932
999
  goal: "Do the thing",
933
1000
  steps: [{ id: "s1" }],
@@ -948,10 +1015,10 @@ describe("blueprint tools", () => {
948
1015
  status: "needs_input",
949
1016
  questions: [{ id: "q1", text: "Which credentials?" }],
950
1017
  },
951
- "/blueprints/bp-1/entries": [
1018
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
952
1019
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
953
1020
  ],
954
- "/blueprints/bp-1": bpFixture({
1021
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
955
1022
  state: "building",
956
1023
  buildStatus: "needs_input",
957
1024
  middlewareId: "mw-1",
@@ -969,11 +1036,11 @@ describe("blueprint tools", () => {
969
1036
  // The service folds !routeId to never_synced regardless of drift; the
970
1037
  // specStale boolean is what must drive the synthesize-first branch.
971
1038
  const payload = await resolve({
972
- "/blueprints/bp-1/entries": [
1039
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
973
1040
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
974
1041
  { id: "e2", kind: "note", title: "Late addition", status: "ready", seq: 2 },
975
1042
  ],
976
- "/blueprints/bp-1": bpFixture({
1043
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
977
1044
  state: "never_synced",
978
1045
  specStale: true,
979
1046
  headVersion: 1,
@@ -992,10 +1059,10 @@ describe("blueprint tools", () => {
992
1059
  });
993
1060
  it("never_synced with a ready spec -> job_stale -> blueprint_build", async () => {
994
1061
  const payload = await resolve({
995
- "/blueprints/bp-1/entries": [
1062
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
996
1063
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
997
1064
  ],
998
- "/blueprints/bp-1": bpFixture({
1065
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
999
1066
  document: {
1000
1067
  goal: "Do the thing",
1001
1068
  steps: [{ id: "s1" }],
@@ -1009,7 +1076,7 @@ describe("blueprint tools", () => {
1009
1076
  });
1010
1077
  it("spec_stale from answer notes only -> flags answerNotesOnly and one synthesize", async () => {
1011
1078
  const payload = await resolve({
1012
- "/blueprints/bp-1/entries": [
1079
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
1013
1080
  {
1014
1081
  id: "e2",
1015
1082
  kind: "note",
@@ -1019,7 +1086,7 @@ describe("blueprint tools", () => {
1019
1086
  content: { answersQuestion: "Which account?" },
1020
1087
  },
1021
1088
  ],
1022
- "/blueprints/bp-1": bpFixture({
1089
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
1023
1090
  state: "spec_stale",
1024
1091
  specStale: true,
1025
1092
  routeId: "route-1",
@@ -1044,10 +1111,10 @@ describe("blueprint tools", () => {
1044
1111
  });
1045
1112
  it("job_ahead -> import-cases fold-back or rebuild", async () => {
1046
1113
  const payload = await resolve({
1047
- "/blueprints/bp-1/entries": [
1114
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
1048
1115
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
1049
1116
  ],
1050
- "/blueprints/bp-1": bpFixture({
1117
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
1051
1118
  state: "job_ahead",
1052
1119
  jobEditedDirectly: true,
1053
1120
  routeId: "route-1",
@@ -1062,7 +1129,7 @@ describe("blueprint tools", () => {
1062
1129
  });
1063
1130
  it("failed enrichment surfaces an enrichment_failed blocker", async () => {
1064
1131
  const payload = await resolve({
1065
- "/blueprints/bp-1/entries": [
1132
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
1066
1133
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
1067
1134
  {
1068
1135
  id: "e2",
@@ -1073,7 +1140,7 @@ describe("blueprint tools", () => {
1073
1140
  seq: 2,
1074
1141
  },
1075
1142
  ],
1076
- "/blueprints/bp-1": bpFixture({
1143
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
1077
1144
  document: { goal: "g", steps: [{ id: "s1" }] },
1078
1145
  }),
1079
1146
  });
@@ -1088,10 +1155,10 @@ describe("blueprint tools", () => {
1088
1155
  status: "queued",
1089
1156
  createdAt: new Date(Date.now() - 10 * 60 * 1000).toISOString(),
1090
1157
  },
1091
- "/blueprints/bp-1/entries": [
1158
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
1092
1159
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
1093
1160
  ],
1094
- "/blueprints/bp-1": bpFixture({
1161
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
1095
1162
  state: "building",
1096
1163
  buildStatus: "queued",
1097
1164
  middlewareId: "mw-1",
@@ -1108,10 +1175,10 @@ describe("blueprint tools", () => {
1108
1175
  it("in_sync -> nothing to do, with the job-side handoff", async () => {
1109
1176
  const payload = await resolve({
1110
1177
  "/routes/route-1/builds/b-1": { id: "b-1", status: "green" },
1111
- "/blueprints/bp-1/entries": [
1178
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9/entries": [
1112
1179
  { id: "e1", kind: "doc", title: "SOP", status: "ready", seq: 1 },
1113
1180
  ],
1114
- "/blueprints/bp-1": bpFixture({
1181
+ "/blueprints/5668a1ae-3912-4713-b269-f98817b9ccd9": bpFixture({
1115
1182
  state: "in_sync",
1116
1183
  routeId: "route-1",
1117
1184
  middlewareId: "mw-1",
@@ -1146,7 +1213,7 @@ describe("blueprint tools", () => {
1146
1213
  "/routes": [{ id: "route-1", method: "POST", path: "/coi" }],
1147
1214
  "/api/workspaces/11/blueprints": [
1148
1215
  {
1149
- id: "bp-9",
1216
+ id: "be9b3716-a487-4eac-886d-e6324bdc01d9",
1150
1217
  name: "COI intake",
1151
1218
  state: "in_sync",
1152
1219
  middlewareId: "mw-1",
@@ -1167,19 +1234,19 @@ describe("blueprint tools", () => {
1167
1234
  });
1168
1235
  const payload = parseToolJson(result);
1169
1236
  expect(payload.blueprint).toEqual({
1170
- blueprintId: "bp-9",
1237
+ blueprintId: "be9b3716-a487-4eac-886d-e6324bdc01d9",
1171
1238
  state: "in_sync",
1172
- hint: expect.stringContaining("resolve_blueprint_state blueprintId=bp-9"),
1239
+ hint: expect.stringContaining("resolve_blueprint_state blueprintId=be9b3716-a487-4eac-886d-e6324bdc01d9"),
1173
1240
  });
1174
1241
  const actions = payload.nextActions.join("\n");
1175
- expect(actions).toContain("OWNED by blueprint bp-9");
1242
+ expect(actions).toContain("OWNED by blueprint be9b3716-a487-4eac-886d-e6324bdc01d9");
1176
1243
  // The route has no test cases and no definition — on an owned route the
1177
1244
  // resolver must route BOTH gaps through the spec, never recommend the
1178
1245
  // direct job edits its own ownership warning forbids.
1179
1246
  expect(actions).not.toContain("add_test_case (input + assertions)");
1180
1247
  expect(actions).not.toContain("teach_job (example input + intent)");
1181
1248
  expect(actions).not.toContain("update_job variant=draft with a step graph");
1182
- expect(actions).toContain("blueprint_build blueprintId=bp-9");
1249
+ expect(actions).toContain("blueprint_build blueprintId=be9b3716-a487-4eac-886d-e6324bdc01d9");
1183
1250
  }
1184
1251
  finally {
1185
1252
  await client.close();