@kindgi/cli 0.1.4 → 0.1.5-rc.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.
Files changed (319) hide show
  1. package/README.md +13 -7
  2. package/dist/build/defaults.d.ts +3 -1
  3. package/dist/build/defaults.d.ts.map +1 -1
  4. package/dist/build/defaults.js +44 -1
  5. package/dist/build/defaults.js.map +1 -1
  6. package/dist/build/java-image.d.ts +31 -0
  7. package/dist/build/java-image.d.ts.map +1 -0
  8. package/dist/build/java-image.js +146 -0
  9. package/dist/build/java-image.js.map +1 -0
  10. package/dist/build/pack-root.d.ts +1 -31
  11. package/dist/build/pack-root.d.ts.map +1 -1
  12. package/dist/build/pack-root.js +22 -2
  13. package/dist/build/pack-root.js.map +1 -1
  14. package/dist/build/runners.d.ts +44 -0
  15. package/dist/build/runners.d.ts.map +1 -1
  16. package/dist/build/scala-image.d.ts +21 -0
  17. package/dist/build/scala-image.d.ts.map +1 -0
  18. package/dist/build/scala-image.js +150 -0
  19. package/dist/build/scala-image.js.map +1 -0
  20. package/dist/cli-pin.d.ts +16 -0
  21. package/dist/cli-pin.d.ts.map +1 -0
  22. package/dist/cli-pin.js +66 -0
  23. package/dist/cli-pin.js.map +1 -0
  24. package/dist/commands/agents.d.ts.map +1 -1
  25. package/dist/commands/agents.js +9 -8
  26. package/dist/commands/agents.js.map +1 -1
  27. package/dist/commands/approvals.d.ts.map +1 -1
  28. package/dist/commands/approvals.js +30 -4
  29. package/dist/commands/approvals.js.map +1 -1
  30. package/dist/commands/artifacts.d.ts.map +1 -1
  31. package/dist/commands/artifacts.js +100 -37
  32. package/dist/commands/artifacts.js.map +1 -1
  33. package/dist/commands/auth.js +2 -2
  34. package/dist/commands/blocks.d.ts.map +1 -1
  35. package/dist/commands/blocks.js +5 -4
  36. package/dist/commands/blocks.js.map +1 -1
  37. package/dist/commands/build.d.ts +10 -2
  38. package/dist/commands/build.d.ts.map +1 -1
  39. package/dist/commands/build.js +109 -16
  40. package/dist/commands/build.js.map +1 -1
  41. package/dist/commands/capabilities.d.ts.map +1 -1
  42. package/dist/commands/capabilities.js +37 -10
  43. package/dist/commands/capabilities.js.map +1 -1
  44. package/dist/commands/console.d.ts +21 -0
  45. package/dist/commands/console.d.ts.map +1 -0
  46. package/dist/commands/console.js +87 -0
  47. package/dist/commands/console.js.map +1 -0
  48. package/dist/commands/conversations.d.ts.map +1 -1
  49. package/dist/commands/conversations.js +13 -2
  50. package/dist/commands/conversations.js.map +1 -1
  51. package/dist/commands/deploy.d.ts.map +1 -1
  52. package/dist/commands/deploy.js +2 -2
  53. package/dist/commands/deploy.js.map +1 -1
  54. package/dist/commands/dev.d.ts +11 -0
  55. package/dist/commands/dev.d.ts.map +1 -1
  56. package/dist/commands/dev.js +211 -39
  57. package/dist/commands/dev.js.map +1 -1
  58. package/dist/commands/doctor.d.ts +14 -2
  59. package/dist/commands/doctor.d.ts.map +1 -1
  60. package/dist/commands/doctor.js +355 -19
  61. package/dist/commands/doctor.js.map +1 -1
  62. package/dist/commands/env-scoped.d.ts +44 -0
  63. package/dist/commands/env-scoped.d.ts.map +1 -0
  64. package/dist/commands/env-scoped.js +222 -0
  65. package/dist/commands/env-scoped.js.map +1 -0
  66. package/dist/commands/env.d.ts.map +1 -1
  67. package/dist/commands/env.js +85 -123
  68. package/dist/commands/env.js.map +1 -1
  69. package/dist/commands/eval-runs.d.ts +9 -0
  70. package/dist/commands/eval-runs.d.ts.map +1 -1
  71. package/dist/commands/eval-runs.js +20 -19
  72. package/dist/commands/eval-runs.js.map +1 -1
  73. package/dist/commands/eval-suites.d.ts.map +1 -1
  74. package/dist/commands/eval-suites.js +16 -8
  75. package/dist/commands/eval-suites.js.map +1 -1
  76. package/dist/commands/exports.d.ts +3 -0
  77. package/dist/commands/exports.d.ts.map +1 -0
  78. package/dist/commands/exports.js +82 -0
  79. package/dist/commands/exports.js.map +1 -0
  80. package/dist/commands/feedback.d.ts.map +1 -1
  81. package/dist/commands/feedback.js +6 -5
  82. package/dist/commands/feedback.js.map +1 -1
  83. package/dist/commands/flows.d.ts.map +1 -1
  84. package/dist/commands/flows.js +2 -1
  85. package/dist/commands/flows.js.map +1 -1
  86. package/dist/commands/gate-policies.d.ts.map +1 -1
  87. package/dist/commands/gate-policies.js +2 -1
  88. package/dist/commands/gate-policies.js.map +1 -1
  89. package/dist/commands/guardrails.d.ts.map +1 -1
  90. package/dist/commands/guardrails.js +3 -2
  91. package/dist/commands/guardrails.js.map +1 -1
  92. package/dist/commands/helpers.js +5 -5
  93. package/dist/commands/helpers.js.map +1 -1
  94. package/dist/commands/index.d.ts.map +1 -1
  95. package/dist/commands/index.js +14 -0
  96. package/dist/commands/index.js.map +1 -1
  97. package/dist/commands/init.d.ts +1 -1
  98. package/dist/commands/init.d.ts.map +1 -1
  99. package/dist/commands/init.js +254 -18
  100. package/dist/commands/init.js.map +1 -1
  101. package/dist/commands/judge-classes.d.ts.map +1 -1
  102. package/dist/commands/judge-classes.js +11 -10
  103. package/dist/commands/judge-classes.js.map +1 -1
  104. package/dist/commands/judgments.d.ts.map +1 -1
  105. package/dist/commands/judgments.js +7 -6
  106. package/dist/commands/judgments.js.map +1 -1
  107. package/dist/commands/key.js +4 -4
  108. package/dist/commands/memory.d.ts.map +1 -1
  109. package/dist/commands/memory.js +277 -25
  110. package/dist/commands/memory.js.map +1 -1
  111. package/dist/commands/people.d.ts +3 -0
  112. package/dist/commands/people.d.ts.map +1 -0
  113. package/dist/commands/people.js +166 -0
  114. package/dist/commands/people.js.map +1 -0
  115. package/dist/commands/proposals.d.ts.map +1 -1
  116. package/dist/commands/proposals.js +390 -69
  117. package/dist/commands/proposals.js.map +1 -1
  118. package/dist/commands/provenance.d.ts.map +1 -1
  119. package/dist/commands/provenance.js +6 -7
  120. package/dist/commands/provenance.js.map +1 -1
  121. package/dist/commands/providers.d.ts.map +1 -1
  122. package/dist/commands/providers.js +6 -5
  123. package/dist/commands/providers.js.map +1 -1
  124. package/dist/commands/reviewers.d.ts.map +1 -1
  125. package/dist/commands/reviewers.js +3 -2
  126. package/dist/commands/reviewers.js.map +1 -1
  127. package/dist/commands/runs.d.ts.map +1 -1
  128. package/dist/commands/runs.js +24 -14
  129. package/dist/commands/runs.js.map +1 -1
  130. package/dist/commands/schedules.d.ts +3 -0
  131. package/dist/commands/schedules.d.ts.map +1 -0
  132. package/dist/commands/schedules.js +275 -0
  133. package/dist/commands/schedules.js.map +1 -0
  134. package/dist/commands/secrets.d.ts.map +1 -1
  135. package/dist/commands/secrets.js +10 -11
  136. package/dist/commands/secrets.js.map +1 -1
  137. package/dist/commands/service-accounts.d.ts +3 -0
  138. package/dist/commands/service-accounts.d.ts.map +1 -0
  139. package/dist/commands/service-accounts.js +175 -0
  140. package/dist/commands/service-accounts.js.map +1 -0
  141. package/dist/commands/skills.d.ts +1 -1
  142. package/dist/commands/skills.d.ts.map +1 -1
  143. package/dist/commands/skills.js +11 -4
  144. package/dist/commands/skills.js.map +1 -1
  145. package/dist/commands/sso.d.ts +6 -0
  146. package/dist/commands/sso.d.ts.map +1 -0
  147. package/dist/commands/sso.js +318 -0
  148. package/dist/commands/sso.js.map +1 -0
  149. package/dist/commands/tokens.d.ts +11 -0
  150. package/dist/commands/tokens.d.ts.map +1 -1
  151. package/dist/commands/tokens.js +144 -11
  152. package/dist/commands/tokens.js.map +1 -1
  153. package/dist/commands/tools.d.ts.map +1 -1
  154. package/dist/commands/tools.js +4 -3
  155. package/dist/commands/tools.js.map +1 -1
  156. package/dist/commands/unwired.d.ts.map +1 -1
  157. package/dist/commands/unwired.js +1 -30
  158. package/dist/commands/unwired.js.map +1 -1
  159. package/dist/commands/upgrade.d.ts +11 -0
  160. package/dist/commands/upgrade.d.ts.map +1 -0
  161. package/dist/commands/upgrade.js +160 -0
  162. package/dist/commands/upgrade.js.map +1 -0
  163. package/dist/context.d.ts +7 -0
  164. package/dist/context.d.ts.map +1 -1
  165. package/dist/context.js +1 -0
  166. package/dist/context.js.map +1 -1
  167. package/dist/dev/defaults.d.ts +28 -11
  168. package/dist/dev/defaults.d.ts.map +1 -1
  169. package/dist/dev/defaults.js +114 -35
  170. package/dist/dev/defaults.js.map +1 -1
  171. package/dist/dev/google-credentials.d.ts +50 -0
  172. package/dist/dev/google-credentials.d.ts.map +1 -0
  173. package/dist/dev/google-credentials.js +157 -0
  174. package/dist/dev/google-credentials.js.map +1 -0
  175. package/dist/dev/java-builder.d.ts +56 -0
  176. package/dist/dev/java-builder.d.ts.map +1 -0
  177. package/dist/dev/java-builder.js +204 -0
  178. package/dist/dev/java-builder.js.map +1 -0
  179. package/dist/dev/jvm-run-files.d.ts +29 -0
  180. package/dist/dev/jvm-run-files.d.ts.map +1 -0
  181. package/dist/dev/jvm-run-files.js +94 -0
  182. package/dist/dev/jvm-run-files.js.map +1 -0
  183. package/dist/dev/lines.d.ts +13 -0
  184. package/dist/dev/lines.d.ts.map +1 -0
  185. package/dist/dev/lines.js +24 -0
  186. package/dist/dev/lines.js.map +1 -0
  187. package/dist/dev/log-view.d.ts +116 -0
  188. package/dist/dev/log-view.d.ts.map +1 -0
  189. package/dist/dev/log-view.js +233 -0
  190. package/dist/dev/log-view.js.map +1 -0
  191. package/dist/dev/pack-code.d.ts +79 -4
  192. package/dist/dev/pack-code.d.ts.map +1 -1
  193. package/dist/dev/pack-code.js +262 -3
  194. package/dist/dev/pack-code.js.map +1 -1
  195. package/dist/dev/pack-env.d.ts.map +1 -1
  196. package/dist/dev/pack-env.js +4 -0
  197. package/dist/dev/pack-env.js.map +1 -1
  198. package/dist/dev/pack-service.d.ts +11 -1
  199. package/dist/dev/pack-service.d.ts.map +1 -1
  200. package/dist/dev/pack-service.js +56 -20
  201. package/dist/dev/pack-service.js.map +1 -1
  202. package/dist/dev/paths.d.ts +6 -0
  203. package/dist/dev/paths.d.ts.map +1 -1
  204. package/dist/dev/paths.js +8 -0
  205. package/dist/dev/paths.js.map +1 -1
  206. package/dist/dev/project-database.js +1 -1
  207. package/dist/dev/project-database.js.map +1 -1
  208. package/dist/dev/runners.d.ts +35 -9
  209. package/dist/dev/runners.d.ts.map +1 -1
  210. package/dist/dev/runtime-container.d.ts +10 -2
  211. package/dist/dev/runtime-container.d.ts.map +1 -1
  212. package/dist/dev/runtime-container.js +48 -20
  213. package/dist/dev/runtime-container.js.map +1 -1
  214. package/dist/dev/runtime-env.d.ts +10 -0
  215. package/dist/dev/runtime-env.d.ts.map +1 -1
  216. package/dist/dev/runtime-env.js +8 -4
  217. package/dist/dev/runtime-env.js.map +1 -1
  218. package/dist/dev/runtime-image.d.ts +1 -1
  219. package/dist/dev/runtime-image.d.ts.map +1 -1
  220. package/dist/dev/runtime-image.js +1 -1
  221. package/dist/dev/runtime-image.js.map +1 -1
  222. package/dist/dev/scala-builder.d.ts +72 -0
  223. package/dist/dev/scala-builder.d.ts.map +1 -0
  224. package/dist/dev/scala-builder.js +347 -0
  225. package/dist/dev/scala-builder.js.map +1 -0
  226. package/dist/env/project-env.d.ts.map +1 -1
  227. package/dist/env/project-env.js +3 -1
  228. package/dist/env/project-env.js.map +1 -1
  229. package/dist/errors.d.ts +9 -0
  230. package/dist/errors.d.ts.map +1 -1
  231. package/dist/errors.js +15 -0
  232. package/dist/errors.js.map +1 -1
  233. package/dist/init/dependency-specs.d.ts +26 -0
  234. package/dist/init/dependency-specs.d.ts.map +1 -1
  235. package/dist/init/dependency-specs.js +38 -0
  236. package/dist/init/dependency-specs.js.map +1 -1
  237. package/dist/init/java-augment.d.ts +24 -0
  238. package/dist/init/java-augment.d.ts.map +1 -0
  239. package/dist/init/java-augment.js +198 -0
  240. package/dist/init/java-augment.js.map +1 -0
  241. package/dist/init/mode-detect.d.ts +2 -2
  242. package/dist/init/mode-detect.d.ts.map +1 -1
  243. package/dist/init/mode-detect.js +12 -1
  244. package/dist/init/mode-detect.js.map +1 -1
  245. package/dist/init/scala-augment.d.ts +23 -0
  246. package/dist/init/scala-augment.d.ts.map +1 -0
  247. package/dist/init/scala-augment.js +213 -0
  248. package/dist/init/scala-augment.js.map +1 -0
  249. package/dist/init/template-files.d.ts +40 -2
  250. package/dist/init/template-files.d.ts.map +1 -1
  251. package/dist/init/template-files.js +56 -5
  252. package/dist/init/template-files.js.map +1 -1
  253. package/dist/main.d.ts +3 -0
  254. package/dist/main.d.ts.map +1 -1
  255. package/dist/main.js +20 -2
  256. package/dist/main.js.map +1 -1
  257. package/dist/open-url.d.ts +15 -0
  258. package/dist/open-url.d.ts.map +1 -0
  259. package/dist/open-url.js +41 -0
  260. package/dist/open-url.js.map +1 -0
  261. package/dist/package-manager.d.ts +10 -3
  262. package/dist/package-manager.d.ts.map +1 -1
  263. package/dist/package-manager.js +19 -1
  264. package/dist/package-manager.js.map +1 -1
  265. package/dist/providers/presets/anthropic.json +11 -3
  266. package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +26 -2
  267. package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +1 -1
  268. package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +47 -3
  269. package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +7 -5
  270. package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +40 -15
  271. package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +27 -1
  272. package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +5 -4
  273. package/dist/sdk-skills/kindgi-getting-started/SKILL.md +1 -1
  274. package/dist/sdk-skills/kindgi-java-authoring-agents/SKILL.md +220 -0
  275. package/dist/sdk-skills/kindgi-java-authoring-flows/SKILL.md +390 -0
  276. package/dist/sdk-skills/kindgi-java-authoring-guardrails/SKILL.md +209 -0
  277. package/dist/sdk-skills/kindgi-java-authoring-tools/SKILL.md +334 -0
  278. package/dist/sdk-skills/kindgi-java-getting-started/SKILL.md +270 -0
  279. package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +21 -3
  280. package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +1 -1
  281. package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +10 -3
  282. package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +36 -5
  283. package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +2 -2
  284. package/dist/sdk-skills/kindgi-scala-authoring-agents/SKILL.md +217 -0
  285. package/dist/sdk-skills/kindgi-scala-authoring-flows/SKILL.md +357 -0
  286. package/dist/sdk-skills/kindgi-scala-authoring-guardrails/SKILL.md +199 -0
  287. package/dist/sdk-skills/kindgi-scala-authoring-tools/SKILL.md +302 -0
  288. package/dist/sdk-skills/kindgi-scala-getting-started/SKILL.md +302 -0
  289. package/dist/templates/java/.mvn/wrapper/maven-wrapper.properties +3 -0
  290. package/dist/templates/java/AGENTS.md +31 -0
  291. package/dist/templates/java/README.md.tmpl +69 -0
  292. package/dist/templates/java/gitignore +9 -0
  293. package/dist/templates/java/kindgi.config.json.tmpl +8 -0
  294. package/dist/templates/java/kindgiw +33 -0
  295. package/dist/templates/java/kindgiw.cmd +28 -0
  296. package/dist/templates/java/mvnw +295 -0
  297. package/dist/templates/java/pom.xml.tmpl +65 -0
  298. package/dist/templates/java/src/main/java/__PACKAGE__/agents/EchoAgent.java.tmpl +27 -0
  299. package/dist/templates/java/src/main/java/__PACKAGE__/flows/EchoFlow.java.tmpl +18 -0
  300. package/dist/templates/java/src/main/java/__PACKAGE__/guardrails/ResponseNotEmpty.java.tmpl +28 -0
  301. package/dist/templates/java/src/main/java/__PACKAGE__/tools/Echo.java.tmpl +22 -0
  302. package/dist/templates/java/src/main/java/__PACKAGE__/tools/Greet.java.tmpl +23 -0
  303. package/dist/templates/java/src/test/java/__PACKAGE__/ToolsTest.java.tmpl +30 -0
  304. package/dist/templates/minimal/README.md.tmpl +6 -12
  305. package/dist/templates/python/README.md.tmpl +4 -4
  306. package/dist/templates/sample/README.md.tmpl +5 -11
  307. package/dist/templates/scala/AGENTS.md +32 -0
  308. package/dist/templates/scala/README.md.tmpl +75 -0
  309. package/dist/templates/scala/build.sbt.tmpl +16 -0
  310. package/dist/templates/scala/gitignore +13 -0
  311. package/dist/templates/scala/kindgi.config.json.tmpl +8 -0
  312. package/dist/templates/scala/project/build.properties +1 -0
  313. package/dist/templates/scala/src/main/scala/__PACKAGE__/agents/EchoAgent.scala.tmpl +23 -0
  314. package/dist/templates/scala/src/main/scala/__PACKAGE__/flows/EchoFlow.scala.tmpl +16 -0
  315. package/dist/templates/scala/src/main/scala/__PACKAGE__/guardrails/ResponseNotEmpty.scala.tmpl +21 -0
  316. package/dist/templates/scala/src/main/scala/__PACKAGE__/tools/Echo.scala.tmpl +16 -0
  317. package/dist/templates/scala/src/main/scala/__PACKAGE__/tools/Greet.scala.tmpl +17 -0
  318. package/dist/templates/scala/src/test/scala/__PACKAGE__/ToolsSuite.scala.tmpl +23 -0
  319. package/package.json +13 -12
@@ -0,0 +1,357 @@
1
+ ---
2
+ name: kindgi-scala-authoring-flows
3
+ description: >
4
+ Covers writing flows for a Kindgi pack in Scala (`kindgi-pack-scala`,
5
+ `com.kindgi.pack.scaladsl`): `Flow(id)` as a `val` of an object named like
6
+ its file, tool and agent steps (`toolNode`, `agentNode`, or a node map
7
+ with `inputMapping` and `config`), edges and their `when` conditions and
8
+ `policy` (`edge(Map(…))`), branches that join again, inputMapping from
9
+ runInput / nodeOutputs, typed agent output in a flow, the flow's declared
10
+ output, loops and fanout, and running a flow (runs start --flow, in the
11
+ background, as a dry run) and reading its journal. Load this whenever you
12
+ are authoring or editing code in a Scala pack's flows packages (a pack
13
+ whose `kindgi.config.json` says `"language": "scala"`), defining a flow,
14
+ or when the user asks to add, change or debug one. Scala tools are
15
+ covered by kindgi-scala-authoring-tools, Scala agents by
16
+ kindgi-scala-authoring-agents.
17
+ type: core
18
+ library: "kindgi-pack-scala"
19
+ version: "0.1.0"
20
+ sdk_version: "0.1.5-rc.0"
21
+ pack_languages: [scala]
22
+ sources:
23
+ - sdks/scala/README.md
24
+ - sdks/scala/kindgi-pack-scala/src/main/scala/com/kindgi/pack/scaladsl/Agent.scala
25
+ - packages/specs/schemas/flow.schema.json
26
+ ---
27
+
28
+ # Authoring Kindgi flows in Scala
29
+
30
+ > **Running `kindgi`:** the pack pins its CLI (`"cli"` in
31
+ > `kindgi.config.json`), and `./kindgiw` runs that version, so every
32
+ > `kindgi <command>` below runs as `./kindgiw <command>`.
33
+ >
34
+ > Scala support is in preview: tested and supported, but the API may still
35
+ > change in 0.1.6 without the usual deprecation period.
36
+
37
+ A **flow** is a versioned, durable graph of steps: tools (your code) and
38
+ agents (a model's judgment), joined by edges that can carry conditions. It
39
+ is **data**, not code: a `val` of type `Flow` in an object named like its
40
+ file, in a `flows` package. The runtime runs it step by step, journals every
41
+ step, and can resume a run that was interrupted. A run pins the flow version
42
+ it started on.
43
+
44
+ Use a flow when the order of the work is known: parse, then classify, then
45
+ branch, then write. Use a single agent when the model should decide the
46
+ order.
47
+
48
+ ## Ask before building
49
+
50
+ - **What goes in, and what comes out?** The run input's shape and the
51
+ output the caller reads. They become `runInput.*` paths and `output`.
52
+ - **Which steps are code, which are judgment?** Deterministic work (parse,
53
+ rank, look up, write) is a tool. Judgment (classify, draft, summarize) is
54
+ an agent with a typed output.
55
+ - **Where does it branch?** Every branch needs a condition, and the steps
56
+ after a branch must cope with the branch that didn't run.
57
+ - **What does it change outside Kindgi?** Know which tools write: a dry run
58
+ stops before them (see "Running a flow").
59
+
60
+ ## A flow
61
+
62
+ The tools and the agent it runs:
63
+
64
+ ```scala
65
+ // src/main/scala/acme/tools/Tickets.scala
66
+ package acme.tools
67
+
68
+ import com.kindgi.pack.scaladsl._
69
+
70
+ /** The ticket tools: parse one, look its invoice up, draft the reply. */
71
+ object Tickets {
72
+ final case class Ticket(customerId: String, body: String)
73
+ final case class ParseInput(ticket: Ticket)
74
+ final case class Parsed(text: String)
75
+ final case class InvoiceInput(customerId: String)
76
+ final case class Invoice(number: String, amount: BigDecimal)
77
+ final case class Found(invoice: Invoice)
78
+ /** `invoice` is absent when the billing step didn't run: an Option. */
79
+ final case class ReplyInput(category: String, invoice: Option[Invoice])
80
+ final case class Reply(text: String)
81
+
82
+ val parse: Tool[ParseInput, Parsed] = Tool[ParseInput, Parsed]("acme.parse-ticket")
83
+ .description("Extracts a ticket's text.")
84
+ .readOnly
85
+ .handler((in, _) => Parsed(in.ticket.body.strip))
86
+
87
+ val lookupInvoice: Tool[InvoiceInput, Found] = Tool[InvoiceInput, Found]("acme.lookup-invoice")
88
+ .description("The customer's latest invoice.")
89
+ .readOnly
90
+ .handler((_, _) => Found(Invoice("INV-1", BigDecimal("42.00"))))
91
+
92
+ val draftReply: Tool[ReplyInput, Reply] = Tool[ReplyInput, Reply]("acme.draft-reply")
93
+ .description("Drafts a reply for the ticket's category.")
94
+ .readOnly
95
+ .handler((in, _) =>
96
+ Reply(in.invoice match {
97
+ case Some(invoice) => s"Invoice ${invoice.number} is ${invoice.amount}."
98
+ case None => s"Thanks, we're on it (${in.category})."
99
+ }))
100
+ }
101
+ ```
102
+
103
+ ```scala
104
+ // src/main/scala/acme/agents/TicketClassifier.scala
105
+ package acme.agents
106
+
107
+ import com.kindgi.pack.scaladsl._
108
+
109
+ /** acme.ticket-classifier: one word for a ticket's category. */
110
+ object TicketClassifier {
111
+ final case class Category(category: String)
112
+
113
+ val agent: Agent = Agent("acme.ticket-classifier")
114
+ .version("0.1.0")
115
+ .name("Ticket classifier")
116
+ .instructions(
117
+ "Classify the support ticket for {{ product }}: answer billing, bug or other, " +
118
+ "as JSON with one field, category.")
119
+ .capability(Map("needs" -> List(Map("feature" -> "tool-use"))))
120
+ .set("parameters", List(Map("name" -> "product", "type" -> "string", "required" -> true)))
121
+ .output[Category]
122
+ .build()
123
+ }
124
+ ```
125
+
126
+ The flow:
127
+
128
+ ```scala
129
+ // src/main/scala/acme/flows/TriageTicket.scala
130
+ package acme.flows
131
+
132
+ import acme.agents.TicketClassifier
133
+ import acme.tools.Tickets
134
+ import com.kindgi.pack.scaladsl._
135
+
136
+ /** acme.triage-ticket: parse, classify, look billing up when needed, reply. */
137
+ object TriageTicket {
138
+ private val isBilling = Map(
139
+ "op" -> "eq",
140
+ "left" -> Map("path" -> "nodeOutputs.classify.output.category"),
141
+ "right" -> Map("literal" -> "billing"))
142
+
143
+ val flow: Flow = Flow("acme.triage-ticket")
144
+ .version("0.1.0")
145
+ .set("name", "Triage a support ticket")
146
+ .set("description", "Parses a ticket, classifies it, looks billing up when needed, drafts a reply.")
147
+ .node(Map("id" -> "parse", "kind" -> "tool", "ref" -> Tickets.parse,
148
+ "inputMapping" -> Map("ticket" -> Map("path" -> "runInput.ticket"))))
149
+ .node(Map("id" -> "classify", "kind" -> "agent", "ref" -> TicketClassifier.agent,
150
+ "inputMapping" -> Map("text" -> Map("path" -> "nodeOutputs.parse.text")),
151
+ "config" -> Map("parameters" -> Map("product" -> "acme-cloud"))))
152
+ .node(Map("id" -> "billing", "kind" -> "tool", "ref" -> Tickets.lookupInvoice,
153
+ "inputMapping" -> Map("customerId" -> Map("path" -> "runInput.ticket.customerId"))))
154
+ .node(Map("id" -> "reply", "kind" -> "tool", "ref" -> Tickets.draftReply,
155
+ "inputMapping" -> Map(
156
+ "category" -> Map("path" -> "nodeOutputs.classify.output.category"),
157
+ "invoice" -> Map("path" -> "nodeOutputs.billing.invoice"))))
158
+ .edge("e0", "$start", "parse")
159
+ .edge("e1", "parse", "classify")
160
+ .edge(Map("id" -> "e2", "from" -> "classify", "to" -> "billing", "when" -> isBilling))
161
+ .edge(Map("id" -> "e3", "from" -> "classify", "to" -> "reply", "when" -> Map("op" -> "not", "child" -> isBilling)))
162
+ .edge("e4", "billing", "reply")
163
+ .edge("e5", "reply", "$end")
164
+ .set("output", Map(
165
+ "mapping" -> Map(
166
+ "category" -> Map("path" -> "nodeOutputs.classify.output.category"),
167
+ "reply" -> Map("path" -> "nodeOutputs.reply.text")),
168
+ "schema" -> Map(
169
+ "type" -> "object",
170
+ "properties" -> Map("category" -> Map("type" -> "string"), "reply" -> Map("type" -> "string")),
171
+ "required" -> List("category", "reply"))))
172
+ .build()
173
+ }
174
+ ```
175
+
176
+ - **Steps:** `toolNode(id, tool)` and `agentNode(id, agent)` are a step with
177
+ nothing more. A step with an `inputMapping`, a `config`, a loop or a fanout
178
+ is a map, `node(Map(…))`, as `flow.schema.json` describes it. In a node's
179
+ map, a `Tool`, `Agent` or `Flow` (loop bodies and fanout branches included)
180
+ becomes its id. A primitive of another pack is its id string.
181
+ - **Edges:** `edge(id, from, to)` joins two steps. An edge with a condition
182
+ (`when`) or a `policy` is a map, `edge(Map(…))`.
183
+ - **Other fields:** `set(field, value)` takes `name`, `description`,
184
+ `output`, `maxParallelism` and `metadata`. `build()` needs a version.
185
+ - **The maps are the wire's shape,** in Scala maps and lists, so their keys
186
+ are camelCase (`inputMapping`, `loopKind`, `maxIterations`).
187
+ - **Where it's checked:** the indexer checks every flow against
188
+ `flow.schema.json` (version, node and edge shapes, `$start` / `$end`).
189
+ `kindgi dev` reports a mistake as a file error that says what and where,
190
+ while the pack's other primitives keep serving. Whether a step's tool or
191
+ agent exists is checked when a run starts (see "Running a flow").
192
+
193
+ ## Nodes
194
+
195
+ - **A tool step** (`"kind" -> "tool"`) runs the tool `ref`.
196
+ - Its input is what the node's `inputMapping` builds; else, the output of
197
+ the node's single upstream node (the run input after `$start`).
198
+ - The input is checked against the tool's input schema, so a mismatch
199
+ fails the step with `input-validation-failed`.
200
+ - The node's output is what the tool returned, as JSON.
201
+ - **An agent step** (`"kind" -> "agent"`) runs one turn of the agent `ref`,
202
+ as a child run of the flow run.
203
+ - The agent gets the node's input two ways: as structured input
204
+ (`{{ input.text }}` in its instructions), and as its user message (the
205
+ input as JSON).
206
+ - `"config" -> Map("parameters" -> Map(…))` fills the agent's `parameters`
207
+ (string, number or boolean values). `"config" -> Map("version" -> "1.2.0")`
208
+ pins an agent version; without it, the latest active version runs.
209
+ - The node's output: `output` is the agent's typed answer (its
210
+ `output[T]`); `text` is the answer as text; `runId` and `conversationId`
211
+ are the child run's. Read a field as `nodeOutputs.<node>.output.<field>`.
212
+ - An answer that doesn't fit the agent's output, after its repairs, fails
213
+ the step with `output-schema-violation`. An approval inside the agent's
214
+ turn parks the flow until it's decided.
215
+ - **A loop** (`"kind" -> "loop"`) repeats a body.
216
+ - `"loopKind" -> "foreach"` runs it once per element of `iterateOver`
217
+ (`concurrency` up to 32 in parallel). `"loopKind" -> "while"` runs it
218
+ until `exitCondition`.
219
+ - The body (`"body" -> Map("nodes" -> List(…), "edges" -> List(…))`) has
220
+ its own nodes and edges, with `$loop-start` and `$loop-end`. The element
221
+ is the body's input.
222
+ - `maxIterations` and `outputSchema` are required.
223
+ - The loop's output is `finalOutput`, plus `outputs` with
224
+ `"collectAllIterations" -> true`.
225
+ - Node ids must be unique across the whole flow, bodies included.
226
+ - **A fanout** (`"kind" -> "fanout"`) runs several handlers on the same input
227
+ at once. Each is a branch (`branchId`, `handler`: a `Tool` or an id, and
228
+ `outputSchema`). `convergence` decides the result: `all-succeed`,
229
+ `any-succeed` (the first success wins), or `settle-all` (wait for every
230
+ branch and report each).
231
+ - **A sub-flow** (`"kind" -> "subgraph"`) is part of the flow schema, but a
232
+ run refuses it today (`flow-unbound`). Inline the steps instead.
233
+
234
+ ## Edges and conditions
235
+
236
+ An edge goes from a node (or `$start`) to a node (or `$end`). Without `when`
237
+ it fires when its source completes. With `when`, it fires only if the
238
+ condition is true. Conditions are maps:
239
+
240
+ | Operator | Shape |
241
+ |---|---|
242
+ | `eq` `ne` `lt` `lte` `gt` `gte` | `op`, `left`, `right` |
243
+ | `in` `notIn` | `op`, `value`, `set` |
244
+ | `exists` `notExists` `truthy` `falsy` | `op`, `value` |
245
+ | `and` `or` | `op`, `children` (a list) |
246
+ | `not` | `op`, `child` |
247
+
248
+ Each operand is `Map("literal" -> …)` or `Map("path" -> …)`. When a path
249
+ doesn't resolve, `eq`, `lt`, `lte`, `gt` and `gte` are false, and `ne` is
250
+ true. So for the "otherwise" branch, wrap the condition in `not` (as above)
251
+ rather than writing a second comparison: it covers exactly what the first
252
+ edge doesn't. A condition used twice is easiest as a `val` (`isBilling`).
253
+
254
+ **Joining branches.** A node with several incoming edges runs once every one
255
+ of them is decided and at least one fired. Above, `reply` runs after
256
+ `billing` on the billing branch, and straight after `classify` otherwise. A
257
+ node none of whose incoming edges fired is skipped, and so is everything
258
+ only it leads to.
259
+
260
+ **Edge policy** (`"policy"` on the edge into a node with a single incoming
261
+ edge; a node with several ignores it):
262
+ - `"retry" -> Map("maxAttempts" -> …, "delayMs" -> …)`, with `backoff` and
263
+ `maxDelayMs` optional; up to 10 attempts in all;
264
+ - `"timeoutMs"`: a step that takes longer fails with `reason: timeout`;
265
+ - `"concurrencyKey"`: at most one such step at a time in the tenant;
266
+ - `"priority"`: −100 to 100.
267
+
268
+ ## Inputs and the output
269
+
270
+ `inputMapping` maps each key to a `literal` or a `path`. Its keys are the
271
+ tool's input **as it travels**: the case class's Jackson names (a parameter
272
+ `customerId` is the key `customerId`; with `@JsonProperty("customer_id")`,
273
+ it's `customer_id`). Paths are dot-separated (a number segment indexes an
274
+ array: `items.0.sku`), rooted at:
275
+ - `runInput.…`: the input the run started with;
276
+ - `nodeOutputs.<nodeId>.…`: a step's output. For an agent step, add
277
+ `.output.<field>` to read its typed answer;
278
+ - `state.…`: values the runtime's own handlers write. Pack tools don't
279
+ write it, so use `nodeOutputs`.
280
+
281
+ A path that doesn't resolve leaves its key out. So a step after a branch
282
+ that didn't run gets no `invoice` key at all. Make that parameter an
283
+ `Option` (as above), or give it a default.
284
+
285
+ `set("output", …)` is what the run returns: a `mapping` resolved when the
286
+ run finishes, checked against `schema` if you give one. A run whose output
287
+ doesn't match fails. Without an output, the run returns the output of the
288
+ step that reached `$end`.
289
+
290
+ ## Running a flow
291
+
292
+ From another terminal in the pack directory, while `kindgi dev` runs:
293
+
294
+ ```sh
295
+ ./kindgiw runs start --flow=acme.triage-ticket --input='{"ticket":{"customerId":"c-1","body":"Charged twice"}}'
296
+ ./kindgiw runs start --flow=acme.triage-ticket --input=@ticket.json --no-wait # the run id now; it finishes in the background
297
+ ./kindgiw runs start --flow=acme.triage-ticket --input=@ticket.json --dry-run # stops before a tool that may write
298
+ ./kindgiw runs get <run-id> # status, output, failureMessage
299
+ ./kindgiw runs journal <run-id> # every step.started / step.completed / edge.evaluated
300
+ ./kindgiw runs stream <run-id> # follow a running one
301
+ ./kindgiw runs cancel <run-id>
302
+ ```
303
+
304
+ - **A refusal before the run exists:** `422 flow-unbound` names the nodes a
305
+ run can't bind: a tool or agent id the tenant doesn't have, or a
306
+ sub-flow. Fix the ids; nothing ran.
307
+ - **A failed step fails the run**, and `failureMessage` says which step and
308
+ why. Retry it on its edge with `policy.retry`, but only if running the
309
+ step twice is safe.
310
+ - **`--no-wait`** is how an application starts runs
311
+ (`"options": {"wait": false}`). It answers with the run id at once; poll
312
+ the run or follow its stream.
313
+ - **`--dry-run`** runs a tool only if it's declared read-only (`readOnly`)
314
+ and has no `writes`, `deletes`, `spawns-run`, `emits-event` or
315
+ `external-side-effect` effect. The first other tool stops the run with
316
+ `dry-run-effectful-tool`, and everything before it really ran.
317
+
318
+ ## Iterating on a flow
319
+
320
+ Save the file, and `kindgi dev` recompiles and re-indexes. The next run uses
321
+ the new definition, with no restart. A run already in flight keeps the
322
+ version it started on. Bump `version` when callers' contract changes (the
323
+ input or the output), not on every save.
324
+
325
+ ## Common mistakes
326
+
327
+ 1. **Building a flow without asking what goes in and comes out.** The pack's
328
+ `EchoFlow` proves the runtime works. It isn't a template for the user's
329
+ flow.
330
+ 2. **A second comparison for "otherwise".** On a path that may be missing,
331
+ `eq` is false and `ne` is true, and `lt`/`gt` are both false, so a
332
+ hand-written opposite can miss a case or overlap. Wrap the positive
333
+ condition in `not`.
334
+ 3. **Reading an agent step's answer at `nodeOutputs.<step>.<field>`.** The
335
+ typed answer is under `.output`. An agent without an output type has
336
+ only `text`.
337
+ 4. **A required input parameter fed by a branch that may not run.** The key
338
+ is left out, the tool's input check fails, and so does the step. Make it
339
+ an `Option`, or give it a default.
340
+ 5. **`inputMapping` keys in the wrong spelling.** They're the input's wire
341
+ names.
342
+ 6. **A `when` given through `set("edges", …)`.** `build()` writes the edges
343
+ added with `edge(…)`; use `edge(Map(…))` for an edge with a condition or
344
+ a policy.
345
+ 7. **A sub-flow node.** A run refuses it (`flow-unbound`).
346
+ 8. **Duplicate node ids inside a loop body.** Ids are unique across the
347
+ whole flow, bodies included.
348
+ 9. **`readOnly` on a tool that writes.** A dry run then runs it for real.
349
+ 10. **A flow as a `def` or a `lazy val`.** It's a file error: make it a
350
+ `val`.
351
+
352
+ ## When the framework itself is the problem
353
+
354
+ If the bug is in Kindgi, kindgi-pack or the Scala layer (a step's output
355
+ missing a field, a condition that evaluates wrongly, a misleading error) and
356
+ not in the pack's code, load `kindgi-framework-feedback` and file it with
357
+ `./kindgiw feedback write`.
@@ -0,0 +1,199 @@
1
+ ---
2
+ name: kindgi-scala-authoring-guardrails
3
+ description: >
4
+ Covers writing guardrails (safety checks on an agent's turn) for a Kindgi
5
+ pack in Scala (`kindgi-pack-scala`, `com.kindgi.pack.scaladsl`):
6
+ `Guardrail[Config](id)` as a `val` of an object named like its file, a
7
+ `(config, trace)` check returning a `CheckResult` (or a Future of one with
8
+ `checkAsync`), `RunTrace`, config case classes and the values a check runs
9
+ with, actions (halt / retry / escalate / log-only / compensate), severity
10
+ and scope, munit tests with `evaluate`, and wiring a guardrail onto an
11
+ agent. Load this whenever you are authoring or editing code in a Scala
12
+ pack's guardrails packages (a pack whose `kindgi.config.json` says
13
+ `"language": "scala"`), defining a check, or wiring a guardrail onto an
14
+ agent. Scala agents are covered by kindgi-scala-authoring-agents, Scala
15
+ tools by kindgi-scala-authoring-tools.
16
+ type: core
17
+ library: "kindgi-pack-scala"
18
+ version: "0.1.0"
19
+ sdk_version: "0.1.5-rc.0"
20
+ pack_languages: [scala]
21
+ sources:
22
+ - sdks/scala/README.md
23
+ - sdks/scala/kindgi-pack-scala/src/main/scala/com/kindgi/pack/scaladsl/Guardrail.scala
24
+ - sdks/java/kindgi-pack/src/main/java/com/kindgi/pack/RunTrace.java
25
+ ---
26
+
27
+ # Authoring Kindgi guardrails in Scala
28
+
29
+ > **Running `kindgi`:** the pack pins its CLI (`"cli"` in
30
+ > `kindgi.config.json`), and `./kindgiw` runs that version, so every
31
+ > `kindgi <command>` below runs as `./kindgiw <command>`.
32
+ >
33
+ > Scala support is in preview: tested and supported, but the API may still
34
+ > change in 0.1.6 without the usual deprecation period.
35
+
36
+ A **guardrail** is a rule an agent's turn must satisfy: a **check** (a
37
+ function over the turn's trace) plus an **action** (what happens when it
38
+ fails). In a Scala pack it is a `val` of type `Guardrail[C]` in an object
39
+ named like its file, in a `guardrails` package. For an agent turn, the
40
+ runtime evaluates every guardrail the agent lists once, on the final answer,
41
+ before it is stored.
42
+
43
+ Ask what the rule should catch before writing one. The sample
44
+ `response-not-empty` guardrail is a demonstration, not a template.
45
+
46
+ ## A guardrail
47
+
48
+ ```scala
49
+ // src/main/scala/acme/guardrails/Citations.scala
50
+ package acme.guardrails
51
+
52
+ import com.kindgi.pack.scaladsl._
53
+ import jakarta.validation.constraints.Min
54
+ import scala.jdk.CollectionConverters._
55
+
56
+ /** acme.no-fabricated-quotes: an answer that quotes case law looked the citations up. */
57
+ object Citations {
58
+ final case class Config(@Min(value = 0) minLookups: Int = 1)
59
+
60
+ val noFabricatedQuotes: Guardrail[Config] = Guardrail[Config]("acme.no-fabricated-quotes")
61
+ .name("No fabricated quotations")
62
+ .onViolation("halt")
63
+ .severity("critical")
64
+ // What the check runs with, keyed as on the wire.
65
+ .set("config", Map("minLookups" -> 2))
66
+ .check { (config, trace) =>
67
+ val lookups = trace.toolCalls.asScala.count {
68
+ case call: java.util.Map[_, _] => call.get("toolName") == "acme.verify-citation"
69
+ case _ => false
70
+ }
71
+ if (lookups >= config.minLookups) CheckResult.pass
72
+ else CheckResult.fail(s"Only $lookups citation lookups (need ${config.minLookups}+).")
73
+ }
74
+ }
75
+ ```
76
+
77
+ - **The check** is `(config, trace) => CheckResult`: `CheckResult.pass` or
78
+ `CheckResult.fail(reason)`. A failed result's reason is what the violation
79
+ reports, so make it say what was wrong. `checkAsync` takes a
80
+ `(config, trace) => Future[CheckResult]`.
81
+ - **`trace`** is a `RunTrace` (kindgi-pack's), with Java collections:
82
+ - `output`: the final answer's text (`null` when there's none: wrap it in
83
+ `Option`);
84
+ - `userInput`;
85
+ - `toolCalls`: each a map with `toolId`, `toolName` (the tool's dotted
86
+ id), `arguments`;
87
+ - `toolResults`: each with `toolCallId`, `output`;
88
+ - `modelCalls`;
89
+ - `mode`: `runtime` or `ci`;
90
+ - `runId`, `tenantId`;
91
+ - `raw`: the trace as sent.
92
+ - **`Guardrail[Config](id)`** takes the config's case class. Its schema is
93
+ derived as a tool input's is, and a parameter's default is the value when
94
+ none is given. `Guardrail.json(id).configSchema(Map(…))` gives a schema
95
+ instead; the config is then a `Map[String, Any]`.
96
+ - **`set("config", Map(…))`** holds the values the check runs with, keyed as
97
+ on the wire. They go into the index, and the service checks them against
98
+ the schema before every evaluation. Without them the check runs with `{}`
99
+ and the defaults. So give every parameter a default: a required one with
100
+ no value fails every evaluation (`input-validation-failed`).
101
+ - An exception thrown from the check fails the evaluation (`handler-throw`).
102
+ For a rule that isn't met, return `CheckResult.fail(…)`.
103
+ - A check gets no model and no provider: it can't call an LLM. Keep it a
104
+ pure function of the trace (fast, deterministic, free).
105
+
106
+ ## `Guardrail[Config](id)`
107
+
108
+ - **`id`:** `<pack-id>.<guardrail-name>`, kebab-case. Name the rule as a
109
+ positive assertion: `no-fabricated-quotes`, `response-not-empty`.
110
+ - **`onViolation(action)`:** `halt`, `retry`, `escalate`, `log-only` or
111
+ `compensate`. For one that needs settings, pass the whole object instead,
112
+ with `action(Map("on-violation" -> "retry", "retry" -> Map("maxAttempts" -> 2)))`.
113
+ The same goes for `escalateTo` and `compensateWith` (a tool id). The
114
+ builder refuses a check with neither.
115
+ - In an agent turn, a failed `halt` guardrail fails the turn
116
+ (`guardrail-violation`), and the answer is not stored.
117
+ - Any other action reports the failure in the turn result's `violations`,
118
+ and the turn completes.
119
+ - In 0.1 the runtime acts only on `halt`: `retry`, `escalate` and
120
+ `compensate` are recorded on the violation, with no second attempt,
121
+ escalation or compensating call.
122
+ - **`severity`:** `info`, `warn`, `error` (default) or `critical`. It's
123
+ independent of the action: dashboards group by severity, and execution
124
+ follows the action.
125
+ - **`set("scope", Map("when" -> "runtime-only"))`:** when the guardrail
126
+ applies: `always`, `ci-only` or `runtime-only`, narrowed by `agents`,
127
+ `flows` and `tenants` lists.
128
+ - **`name`:** a display name. **`checkId`:** defaults to the id.
129
+ - **`kind`:** `zero-llm`, the default: a check over the trace, which is what
130
+ a pack writes.
131
+ - `set("sandbox" | "limits" | "network", …)` are recorded in the index.
132
+
133
+ ## Testing
134
+
135
+ `evaluate` runs the check with the config as given, with no schema check
136
+ and no defaults. An async check is awaited.
137
+
138
+ ```scala
139
+ // src/test/scala/acme/CitationsSuite.scala
140
+ package acme
141
+
142
+ import acme.guardrails.Citations
143
+ import com.kindgi.pack.RunTrace
144
+ import java.util.{List => JList, Map => JMap}
145
+
146
+ class CitationsSuite extends munit.FunSuite {
147
+ test("no lookups fails") {
148
+ val trace = new RunTrace(JMap.of("output", "As held in Smith v. Jones…"))
149
+ assert(!Citations.noFabricatedQuotes.evaluate(Citations.Config(1), trace).passed)
150
+ }
151
+
152
+ test("enough lookups pass") {
153
+ val lookup = JMap.of("toolName", "acme.verify-citation", "arguments", JMap.of())
154
+ val trace = new RunTrace(JMap.of("toolCalls", JList.of(lookup, lookup)))
155
+ assert(Citations.noFabricatedQuotes.evaluate(Citations.Config(2), trace).passed)
156
+ }
157
+ }
158
+ ```
159
+
160
+ `new RunTrace(map)` takes the trace as the runtime sends it: a Java map,
161
+ camelCase keys included.
162
+
163
+ ## Wiring onto an agent
164
+
165
+ ```scala
166
+ Agent("acme.brief-writer")
167
+ // …
168
+ .guardrail(Citations.noFabricatedQuotes)
169
+ ```
170
+
171
+ Pass the `Guardrail`, or its id (`.guardrail("acme.no-fabricated-quotes")`).
172
+ `kindgi dev` registers the pack's guardrails. An agent that names an id with
173
+ no registered guardrail fails the turn before the model is called
174
+ (`Agent "…" references guardrails not in the registry: <id>`).
175
+
176
+ ## Common mistakes
177
+
178
+ 1. **A config parameter with no default and no value.** Without
179
+ `set("config", …)`, the check runs with `{}`. Give the parameter a
180
+ default, or the guardrail its values.
181
+ 2. **`set("config", …)` keyed by Scala names that differ from the wire.**
182
+ It's keyed by the case class's Jackson names.
183
+ 3. **No `onViolation` or `action`.** The builder refuses the check.
184
+ 4. **Expecting another attempt.** `halt` stops the turn, and in 0.1 `retry`
185
+ doesn't run the turn again: it's only recorded.
186
+ 5. **Calling a model from the check.** That isn't available: keep checks
187
+ pure.
188
+ 6. **Throwing for a broken rule.** Return `CheckResult.fail(…)`. An
189
+ exception is an evaluation error, not a violation.
190
+ 7. **`trace.output.length` without a null check.** `output` is `null` when
191
+ the turn produced no text: `Option(trace.output).getOrElse("")`.
192
+ 8. **A guardrail as a `def` or a `lazy val`.** It's a file error: make it a
193
+ `val`.
194
+
195
+ ## When the framework itself is the problem
196
+
197
+ If the bug is in Kindgi, kindgi-pack or the Scala layer (a trace field
198
+ missing, a misleading error) and not in the check, load
199
+ `kindgi-framework-feedback` and file it with `./kindgiw feedback write`.