@mawaru/sdk 0.8.0 → 0.9.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.
@@ -261,6 +261,37 @@ describe("humanConfigSchema", () => {
261
261
  humanConfigSchema.parse({ assignment: { units: [["not-a-uuid"]] } }),
262
262
  ).toThrow()
263
263
  })
264
+
265
+ it("run_starter:固定メンバーと混ぜて受け付ける(実行時に start_by で解決)", () => {
266
+ const u1 = uuid()
267
+ const parsed = humanConfigSchema.parse({
268
+ assignment: {
269
+ strategy: "fixed",
270
+ approval_mode: "all",
271
+ units: [["run_starter", u1]],
272
+ },
273
+ })
274
+ expect(parsed.assignment.units).toEqual([["run_starter", u1]])
275
+ })
276
+
277
+ it("run_starter:交代制(round_robin)との併用を拒否する", () => {
278
+ expect(() =>
279
+ humanConfigSchema.parse({
280
+ assignment: {
281
+ strategy: "round_robin",
282
+ units: [["run_starter"], [uuid()]],
283
+ },
284
+ }),
285
+ ).toThrow()
286
+ })
287
+
288
+ it("run_starter:ユニット内の重複を拒否する", () => {
289
+ expect(() =>
290
+ humanConfigSchema.parse({
291
+ assignment: { units: [["run_starter", "run_starter"]] },
292
+ }),
293
+ ).toThrow()
294
+ })
264
295
  })
265
296
 
266
297
  describe("saveGraphBodySchema", () => {
package/_schemas/graph.ts CHANGED
@@ -130,9 +130,16 @@ export const aiConfigSchema = z.object({
130
130
  })
131
131
  export type AiConfig = z.infer<typeof aiConfigSchema>
132
132
 
133
+ // 担当ユニットのメンバー:user_id の名指し、または動的な担当「ループを実行した人」
134
+ // (run の起動者。実行時に runs.start_by で解決し、解決できなければその1人だけ落ちる。
135
+ // docs/tasks/wip/Human担当に「ループを実行した人」を追加する.md)
136
+ export const RUN_STARTER = "run_starter"
137
+ export const assignMemberSchema = z.union([z.uuid(), z.literal(RUN_STARTER)])
138
+ export type AssignMember = z.infer<typeof assignMemberSchema>
139
+
133
140
  // Human ノードの kind 別設定(node_humans + node_human_assignees に対応)。
134
141
  // 担当 = ユニット(1人以上のメンバー)の順序付きリスト + 承認モード + 選出戦略。
135
- // units[i] は user_id の配列(1人=個人・複数=グループ)。units 空 = tenant の誰でも。
142
+ // units[i] はメンバーの配列(1人=個人・複数=グループ)。units 空 = tenant の誰でも。
136
143
  // approval_mode はノード単位(any=誰か1人 / all=全員。差し戻しは1人で成立=veto)。
137
144
  // メンバーが tenant の membership であることの検証は保存 API 側で行う
138
145
  // (docs/tasks/backlog/Human担当者の高度化(複数人承認とラウンドロビン).md)
@@ -147,7 +154,7 @@ export const humanConfigSchema = z.object({
147
154
  .object({
148
155
  strategy: z.enum(["fixed", "round_robin"]).default("fixed"),
149
156
  approval_mode: z.enum(["any", "all"]).default("any"),
150
- units: z.array(z.array(z.uuid()).min(1)).default([]),
157
+ units: z.array(z.array(assignMemberSchema).min(1)).default([]),
151
158
  })
152
159
  .superRefine((assignment, ctx) => {
153
160
  if (assignment.strategy === "fixed" && assignment.units.length > 1) {
@@ -167,13 +174,25 @@ export const humanConfigSchema = z.object({
167
174
  message: "round_robin にはユニットが2つ以上必要です",
168
175
  })
169
176
  }
177
+ // 交代制と「ループを実行した人」は排他:どの回に誰が担当かが起動者と交代位置の
178
+ // 掛け算になって読めなくなるため、設定できる形の方を減らす
179
+ if (
180
+ assignment.strategy === "round_robin" &&
181
+ assignment.units.some((unit) => unit.includes(RUN_STARTER))
182
+ ) {
183
+ ctx.addIssue({
184
+ code: "custom",
185
+ path: ["units"],
186
+ message: "round_robin の担当に「ループを実行した人」は入れられません",
187
+ })
188
+ }
170
189
  // ユニット内の重複は拒否(またぐ重複は「Aは毎回・相手が交代」の形があるので許す)
171
190
  assignment.units.forEach((unit, i) => {
172
191
  if (new Set(unit).size !== unit.length) {
173
192
  ctx.addIssue({
174
193
  code: "custom",
175
194
  path: ["units", i],
176
- message: "ユニット内で user_id が重複しています",
195
+ message: "ユニット内で担当が重複しています",
177
196
  })
178
197
  }
179
198
  })
@@ -47,6 +47,9 @@ export declare const aiConfigSchema: z.ZodObject<{
47
47
  ref: z.ZodOptional<z.ZodString>;
48
48
  }, z.core.$strip>;
49
49
  export type AiConfig = z.infer<typeof aiConfigSchema>;
50
+ export declare const RUN_STARTER = "run_starter";
51
+ export declare const assignMemberSchema: z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>;
52
+ export type AssignMember = z.infer<typeof assignMemberSchema>;
50
53
  export declare const humanConfigSchema: z.ZodObject<{
51
54
  view: z.ZodOptional<z.ZodString>;
52
55
  view_config: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
@@ -59,7 +62,7 @@ export declare const humanConfigSchema: z.ZodObject<{
59
62
  any: "any";
60
63
  all: "all";
61
64
  }>>;
62
- units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUUID>>>;
65
+ units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
63
66
  }, z.core.$strip>;
64
67
  }, z.core.$strip>;
65
68
  export type HumanConfig = z.infer<typeof humanConfigSchema>;
@@ -143,7 +146,7 @@ export declare const graphNodeSchema: z.ZodObject<{
143
146
  any: "any";
144
147
  all: "all";
145
148
  }>>;
146
- units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUUID>>>;
149
+ units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
147
150
  }, z.core.$strip>;
148
151
  }, z.core.$strip>>;
149
152
  component: z.ZodOptional<z.ZodObject<{
@@ -225,7 +228,7 @@ export declare const saveGraphBodySchema: z.ZodObject<{
225
228
  any: "any";
226
229
  all: "all";
227
230
  }>>;
228
- units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUUID>>>;
231
+ units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
229
232
  }, z.core.$strip>;
230
233
  }, z.core.$strip>>;
231
234
  component: z.ZodOptional<z.ZodObject<{
@@ -91,9 +91,14 @@ export const aiConfigSchema = z.object({
91
91
  .refine((v) => !v.includes("/") && !v.includes("\\") && v !== "." && v !== "..", "ai_dir は nodes/ai/ 直下のディレクトリ名で指定してください"),
92
92
  ref: z.string().min(1).optional(),
93
93
  });
94
+ // 担当ユニットのメンバー:user_id の名指し、または動的な担当「ループを実行した人」
95
+ // (run の起動者。実行時に runs.start_by で解決し、解決できなければその1人だけ落ちる。
96
+ // docs/tasks/wip/Human担当に「ループを実行した人」を追加する.md)
97
+ export const RUN_STARTER = "run_starter";
98
+ export const assignMemberSchema = z.union([z.uuid(), z.literal(RUN_STARTER)]);
94
99
  // Human ノードの kind 別設定(node_humans + node_human_assignees に対応)。
95
100
  // 担当 = ユニット(1人以上のメンバー)の順序付きリスト + 承認モード + 選出戦略。
96
- // units[i] は user_id の配列(1人=個人・複数=グループ)。units 空 = tenant の誰でも。
101
+ // units[i] はメンバーの配列(1人=個人・複数=グループ)。units 空 = tenant の誰でも。
97
102
  // approval_mode はノード単位(any=誰か1人 / all=全員。差し戻しは1人で成立=veto)。
98
103
  // メンバーが tenant の membership であることの検証は保存 API 側で行う
99
104
  // (docs/tasks/backlog/Human担当者の高度化(複数人承認とラウンドロビン).md)
@@ -108,7 +113,7 @@ export const humanConfigSchema = z.object({
108
113
  .object({
109
114
  strategy: z.enum(["fixed", "round_robin"]).default("fixed"),
110
115
  approval_mode: z.enum(["any", "all"]).default("any"),
111
- units: z.array(z.array(z.uuid()).min(1)).default([]),
116
+ units: z.array(z.array(assignMemberSchema).min(1)).default([]),
112
117
  })
113
118
  .superRefine((assignment, ctx) => {
114
119
  if (assignment.strategy === "fixed" && assignment.units.length > 1) {
@@ -126,13 +131,23 @@ export const humanConfigSchema = z.object({
126
131
  message: "round_robin にはユニットが2つ以上必要です",
127
132
  });
128
133
  }
134
+ // 交代制と「ループを実行した人」は排他:どの回に誰が担当かが起動者と交代位置の
135
+ // 掛け算になって読めなくなるため、設定できる形の方を減らす
136
+ if (assignment.strategy === "round_robin" &&
137
+ assignment.units.some((unit) => unit.includes(RUN_STARTER))) {
138
+ ctx.addIssue({
139
+ code: "custom",
140
+ path: ["units"],
141
+ message: "round_robin の担当に「ループを実行した人」は入れられません",
142
+ });
143
+ }
129
144
  // ユニット内の重複は拒否(またぐ重複は「Aは毎回・相手が交代」の形があるので許す)
130
145
  assignment.units.forEach((unit, i) => {
131
146
  if (new Set(unit).size !== unit.length) {
132
147
  ctx.addIssue({
133
148
  code: "custom",
134
149
  path: ["units", i],
135
- message: "ユニット内で user_id が重複しています",
150
+ message: "ユニット内で担当が重複しています",
136
151
  });
137
152
  }
138
153
  });
package/dist/cli.js CHANGED
File without changes
package/dist/typegen.js CHANGED
@@ -146,7 +146,14 @@ export const renderTypesFile = (manifest, rawConfig) => {
146
146
  envelopeUnionType(Object.keys(manifest.outputs)),
147
147
  "",
148
148
  ...(refEntries.length > 0
149
- ? [`export type Refs = {\n${refEntries.join("\n")}\n}`, ""]
149
+ ? [
150
+ `export type Refs = {\n${refEntries.join("\n")}\n}`,
151
+ "",
152
+ "// run の第2引数:refs=届いた袋の読み、setRefs=袋へのパッチの宣言",
153
+ "// (書いたキーだけが完了時に袋へマージされ、下流のノードから読める)",
154
+ "export type Ctx = { refs: Refs; setRefs: (patch: Partial<Refs>) => void }",
155
+ "",
156
+ ]
150
157
  : []),
151
158
  ].join("\n");
152
159
  };
@@ -26,10 +26,17 @@ skills/<dir>/ # AI ノードが参照する skill(SKILL.md)
26
26
 
27
27
  - `config.json` — 入出力スキーマ等の宣言。正は `_schemas/program-manifest.ts`
28
28
  (`inputs` は `"in"` の1件だけ・`outputs` は1件以上・`env` は必要な secret 名の宣言)
29
- - `main.ts` — `run(input, ctx)` を export する。`ctx.refs` にループ変数が入る。戻り値:
29
+ - `main.ts` — `run(input, ctx)` を export する。戻り値:
30
30
  - `{ <出口ポートのkey>: <データ> }` の**単一キー封筒**(どの出口から出るかを戻り値自身が運ぶ)
31
31
  - `"pending"` — 完了保留(外部イベントで後から end が届く)
32
- - 型生成:`npx mawaru typegen` が config.json から `types.d.ts`(Input / Outputs 型)を生成する
32
+ - `ctx` ループ変数の袋の読み書き:
33
+ - `ctx.refs` — 届いた袋(loop に定義された変数の、この step 開始時点の値)
34
+ - `ctx.setRefs({ key: value })` — 袋へのパッチの宣言。**完了時にだけ**反映され、以降の
35
+ ノードが `ctx.refs` で読める(動的な値を下流へ渡す唯一の経路)。何度呼んでもトップレベル
36
+ キー後勝ちで畳まれる。`"pending"` を返す実行では反映されない。loop に定義の無いキーを
37
+ 書くと step は failed になる
38
+ - 型生成:`npx mawaru typegen` が config.json から `types.d.ts`(Input / Outputs / Refs / Ctx 型)を
39
+ 生成する
33
40
  - **ノード単位の npm 依存**:`<dir>/package.json` を置けば実行前にそのディレクトリで
34
41
  `npm ci` される。`package-lock.json` の commit が必須(無いと明瞭に失敗する)
35
42
  - 環境変数:`config.json` の `env` に宣言した secret(repo の Actions secrets)だけが
package/package.json CHANGED
@@ -1,20 +1,15 @@
1
1
  {
2
2
  "name": "@mawaru/sdk",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "mawaru 実行 repo の開発 SDK。契約スキーマ(config.json 規約・ループ graph API)の正 + typegen / validate CLI",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "mawaru": "dist/cli.js"
8
8
  },
9
9
  "exports": {
10
- ".": "./index.ts"
11
- },
12
- "publishConfig": {
13
- "exports": {
14
- ".": {
15
- "types": "./dist/index.d.ts",
16
- "default": "./dist/index.js"
17
- }
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
18
13
  }
19
14
  },
20
15
  "files": [
@@ -28,13 +23,6 @@
28
23
  "engines": {
29
24
  "node": ">=20"
30
25
  },
31
- "scripts": {
32
- "build": "tsc -p tsconfig.build.json",
33
- "prepack": "pnpm build",
34
- "typecheck": "tsc --noEmit",
35
- "test": "vitest run",
36
- "test:watch": "vitest"
37
- },
38
26
  "dependencies": {
39
27
  "zod": "^4.4.3",
40
28
  "zod-from-json-schema": "^0.5.3"
@@ -43,5 +31,11 @@
43
31
  "@types/node": "^26.1.0",
44
32
  "typescript": "^6.0.3",
45
33
  "vitest": "^4.1.9"
34
+ },
35
+ "scripts": {
36
+ "build": "tsc -p tsconfig.build.json",
37
+ "typecheck": "tsc --noEmit",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest"
46
40
  }
47
- }
41
+ }