@mawaru/sdk 0.24.0 → 0.25.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.
@@ -160,17 +160,25 @@ describe("aiConfigSchema", () => {
160
160
  })
161
161
 
162
162
  describe("humanConfigSchema", () => {
163
- it("既定値を補う(fixed・any・units 空=tenant の誰でも)", () => {
164
- expect(humanConfigSchema.parse({ assignment: {} })).toEqual({
165
- assignment: { strategy: "fixed", approval_mode: "any", units: [] },
163
+ it("strategy / approval_mode の既定値を補う(fixed・any", () => {
164
+ const u1 = uuid()
165
+ expect(humanConfigSchema.parse({ assignment: { units: [[u1]] } })).toEqual({
166
+ assignment: { strategy: "fixed", approval_mode: "any", units: [[u1]] },
166
167
  })
167
168
  })
168
169
 
170
+ it("担当なし(units 空・省略)を拒否する(「誰でも」は無い)", () => {
171
+ expect(() => humanConfigSchema.parse({ assignment: {} })).toThrow()
172
+ expect(() =>
173
+ humanConfigSchema.parse({ assignment: { units: [] } }),
174
+ ).toThrow(/担当/)
175
+ })
176
+
169
177
  // 承認ビューは不透明なキー(program_dir / ai_dir と同じ)。実在するか・そのテナントが
170
178
  // 選べるかは mawaru 側のカタログ(非公開)と保存 API が見るので、ここは形だけ
171
179
  it("承認ビューを受け付ける", () => {
172
180
  const parsed = humanConfigSchema.parse({
173
- assignment: {},
181
+ assignment: { units: [[uuid()]] },
174
182
  view: "acme/invoice",
175
183
  })
176
184
  expect(parsed.view).toBe("acme/invoice")
@@ -178,7 +186,7 @@ describe("humanConfigSchema", () => {
178
186
 
179
187
  it("空のビューキーは拒否する", () => {
180
188
  expect(() =>
181
- humanConfigSchema.parse({ assignment: {}, view: "" }),
189
+ humanConfigSchema.parse({ assignment: { units: [[uuid()]] }, view: "" }),
182
190
  ).toThrow()
183
191
  })
184
192
 
@@ -513,6 +521,8 @@ const humanNode = () => ({
513
521
  port("out", "reject"),
514
522
  ]),
515
523
  kind: "human" as const,
524
+ // 担当は必須(「担当なし=誰でも」は無い)
525
+ human: { assignment: { units: [[uuid()]] } },
516
526
  })
517
527
  const aiNode = () => ({
518
528
  ...makeNode([
@@ -538,6 +548,13 @@ const outByKey = (node: TestNode, key: string): GraphPort => {
538
548
  }
539
549
 
540
550
  describe("saveGraphBodySchema(kind 別ポート構成)", () => {
551
+ it("human の担当なし(human 設定の省略)を拒否する", () => {
552
+ const { human: _human, ...noAssign } = humanNode()
553
+ expect(() =>
554
+ saveGraphBodySchema.parse({ nodes: [noAssign], connections: [] }),
555
+ ).toThrow(/担当/)
556
+ })
557
+
541
558
  it("human の旧 ok/ng 構成を拒否し、in/approve/reject を受け付ける", () => {
542
559
  expect(() =>
543
560
  saveGraphBodySchema.parse({ nodes: [humanNode()], connections: [] }),
package/_schemas/graph.ts CHANGED
@@ -135,7 +135,8 @@ export type AssignMember = z.infer<typeof assignMemberSchema>
135
135
 
136
136
  // Human ノードの kind 別設定(node_humans + node_human_assignees に対応)。
137
137
  // 担当 = ユニット(1人以上のメンバー)の順序付きリスト + 承認モード + 選出戦略。
138
- // units[i] はメンバーの配列(1人=個人・複数=グループ)。units 空 = tenant の誰でも。
138
+ // units[i] はメンバーの配列(1人=個人・複数=グループ)。**担当は必ず明示する**
139
+ // (「担当なし=tenant の誰でも」は無い。Human担当の必須化(誰でもの廃止).md)。
139
140
  // approval_mode はノード単位(any=誰か1人 / all=全員。差し戻しは1人で成立=veto)。
140
141
  // メンバーが tenant の membership であることの検証は保存 API 側で行う
141
142
  // (docs/tasks/backlog/Human担当者の高度化(複数人承認とラウンドロビン).md)
@@ -148,7 +149,9 @@ export const humanConfigSchema = z.object({
148
149
  .object({
149
150
  strategy: z.enum(["fixed", "round_robin"]).default("fixed"),
150
151
  approval_mode: z.enum(["any", "all"]).default("any"),
151
- units: z.array(z.array(assignMemberSchema).min(1)).default([]),
152
+ units: z
153
+ .array(z.array(assignMemberSchema).min(1))
154
+ .min(1, "担当を1人以上指定してください"),
152
155
  })
153
156
  .superRefine((assignment, ctx) => {
154
157
  if (assignment.strategy === "fixed" && assignment.units.length > 1) {
@@ -403,6 +406,16 @@ export const saveGraphBodySchema = z
403
406
  hookDirs.add(hook.hook_dir)
404
407
  })
405
408
 
409
+ // human は担当を必ず明示する(「担当なし=tenant の誰でも」は無い。
410
+ // Human担当の必須化(誰でもの廃止).md)
411
+ if (node.kind === "human" && node.human === undefined) {
412
+ ctx.addIssue({
413
+ code: "custom",
414
+ path: ["nodes", i, "human"],
415
+ message: "human ノードには担当(assignment.units)が必要です",
416
+ })
417
+ }
418
+
406
419
  // component は参照先が無いと成立しない(他 kind の設定はデフォルトで補えるが
407
420
  // component_loop_id は補えない)
408
421
  if (node.kind === "component" && node.component === undefined) {
@@ -58,7 +58,7 @@ export declare const humanConfigSchema: z.ZodObject<{
58
58
  any: "any";
59
59
  all: "all";
60
60
  }>>;
61
- units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
61
+ units: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>;
62
62
  }, z.core.$strip>;
63
63
  }, z.core.$strip>;
64
64
  export type HumanConfig = z.infer<typeof humanConfigSchema>;
@@ -199,7 +199,7 @@ export declare const graphNodeSchema: z.ZodObject<{
199
199
  any: "any";
200
200
  all: "all";
201
201
  }>>;
202
- units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
202
+ units: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>;
203
203
  }, z.core.$strip>;
204
204
  }, z.core.$strip>>;
205
205
  component: z.ZodOptional<z.ZodObject<{
@@ -298,7 +298,7 @@ export declare const saveGraphBodySchema: z.ZodObject<{
298
298
  any: "any";
299
299
  all: "all";
300
300
  }>>;
301
- units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
301
+ units: z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>;
302
302
  }, z.core.$strip>;
303
303
  }, z.core.$strip>>;
304
304
  component: z.ZodOptional<z.ZodObject<{
@@ -96,7 +96,8 @@ export const RUN_STARTER = "run_starter";
96
96
  export const assignMemberSchema = z.union([z.uuid(), z.literal(RUN_STARTER)]);
97
97
  // Human ノードの kind 別設定(node_humans + node_human_assignees に対応)。
98
98
  // 担当 = ユニット(1人以上のメンバー)の順序付きリスト + 承認モード + 選出戦略。
99
- // units[i] はメンバーの配列(1人=個人・複数=グループ)。units 空 = tenant の誰でも。
99
+ // units[i] はメンバーの配列(1人=個人・複数=グループ)。**担当は必ず明示する**
100
+ // (「担当なし=tenant の誰でも」は無い。Human担当の必須化(誰でもの廃止).md)。
100
101
  // approval_mode はノード単位(any=誰か1人 / all=全員。差し戻しは1人で成立=veto)。
101
102
  // メンバーが tenant の membership であることの検証は保存 API 側で行う
102
103
  // (docs/tasks/backlog/Human担当者の高度化(複数人承認とラウンドロビン).md)
@@ -109,7 +110,9 @@ export const humanConfigSchema = z.object({
109
110
  .object({
110
111
  strategy: z.enum(["fixed", "round_robin"]).default("fixed"),
111
112
  approval_mode: z.enum(["any", "all"]).default("any"),
112
- units: z.array(z.array(assignMemberSchema).min(1)).default([]),
113
+ units: z
114
+ .array(z.array(assignMemberSchema).min(1))
115
+ .min(1, "担当を1人以上指定してください"),
113
116
  })
114
117
  .superRefine((assignment, ctx) => {
115
118
  if (assignment.strategy === "fixed" && assignment.units.length > 1) {
@@ -335,6 +338,15 @@ export const saveGraphBodySchema = z
335
338
  }
336
339
  hookDirs.add(hook.hook_dir);
337
340
  });
341
+ // human は担当を必ず明示する(「担当なし=tenant の誰でも」は無い。
342
+ // Human担当の必須化(誰でもの廃止).md)
343
+ if (node.kind === "human" && node.human === undefined) {
344
+ ctx.addIssue({
345
+ code: "custom",
346
+ path: ["nodes", i, "human"],
347
+ message: "human ノードには担当(assignment.units)が必要です",
348
+ });
349
+ }
338
350
  // component は参照先が無いと成立しない(他 kind の設定はデフォルトで補えるが
339
351
  // component_loop_id は補えない)
340
352
  if (node.kind === "component" && node.component === undefined) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mawaru/sdk",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "mawaru 実行 repo の開発 SDK。契約スキーマ(config.json 規約・ループ graph API)の正 + typegen / validate CLI",
5
5
  "type": "module",
6
6
  "bin": {