insika 0.2.0 → 0.7.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 (300) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -17
  3. data/README.md +52 -11
  4. data/bin/insika +895 -8
  5. data/bin/insika-router +87 -0
  6. data/docs/AGENTS.md +229 -163
  7. data/docs/API.md +56 -0
  8. data/docs/ARCHITECTURE.md +3 -2
  9. data/docs/ARTIFACTS.md +95 -0
  10. data/docs/BENCHMARK.md +2 -2
  11. data/docs/CHANNELS.md +111 -21
  12. data/docs/CONTEXT.md +132 -19
  13. data/docs/DEMO.md +80 -0
  14. data/docs/DEPLOY.md +75 -4
  15. data/docs/EMBEDDING.md +1 -1
  16. data/docs/EVALS.md +146 -2
  17. data/docs/FACTS.md +135 -0
  18. data/docs/HARVEST.md +116 -0
  19. data/docs/KNOWLEDGE.md +290 -0
  20. data/docs/LOADTEST.md +16 -10
  21. data/docs/MEDIA.md +128 -0
  22. data/docs/OBSERVABILITY.md +65 -6
  23. data/docs/OUTCOMES.md +137 -0
  24. data/docs/PLUGINS.md +51 -6
  25. data/docs/POLICY.md +216 -0
  26. data/docs/REFINEMENT.md +17 -12
  27. data/docs/RELEASING.md +27 -0
  28. data/docs/ROUTER.md +213 -0
  29. data/docs/RUNNING-LOCAL.md +6 -6
  30. data/docs/SCHEDULING.md +121 -0
  31. data/docs/SECURITY.md +79 -2
  32. data/docs/SKILLS.md +13 -4
  33. data/docs/SOAK.md +127 -0
  34. data/docs/TEMPLATES.md +134 -0
  35. data/docs/TOOLS.md +220 -27
  36. data/docs/WHY.md +1 -1
  37. data/docs/WORKFLOWS.md +2 -2
  38. data/docs/_includes/head_custom.html +5 -0
  39. data/docs/_includes/title.html +13 -0
  40. data/docs/_sass/color_schemes/insika.scss +32 -0
  41. data/docs/_sass/custom/custom.scss +199 -0
  42. data/docs/_sass/custom/setup.scss +26 -0
  43. data/docs/assets/img/favicon.svg +7 -0
  44. data/docs/assets/img/insika-mark.svg +7 -0
  45. data/docs/core-concepts.md +21 -0
  46. data/docs/domain.md +115 -0
  47. data/docs/improve.md +20 -0
  48. data/docs/index.md +9 -6
  49. data/docs/integrate.md +20 -0
  50. data/docs/operate.md +13 -6
  51. data/docs/prompts/ADD-TOOL.md +118 -0
  52. data/docs/prompts/DIAGNOSE-TURN.md +65 -0
  53. data/docs/prompts/GO-LIVE.md +138 -0
  54. data/docs/prompts/RUN-EXAMPLES.md +70 -0
  55. data/docs/reference.md +19 -0
  56. data/docs/ship.md +10 -2
  57. data/docs/start-here.md +18 -0
  58. data/lib/insika/agent_profile.rb +202 -10
  59. data/lib/insika/artifact_signing.rb +82 -0
  60. data/lib/insika/artifact_store.rb +160 -0
  61. data/lib/insika/balloon_splitter.rb +102 -0
  62. data/lib/insika/budget_ledger.rb +34 -3
  63. data/lib/insika/cache_series_store.rb +49 -0
  64. data/lib/insika/channel_delivery.rb +119 -14
  65. data/lib/insika/channels/relay.rb +77 -3
  66. data/lib/insika/channels/web.rb +2 -2
  67. data/lib/insika/chat_builder.rb +105 -2
  68. data/lib/insika/checkpoint_store.rb +16 -0
  69. data/lib/insika/commands/agent_payload.rb +3 -3
  70. data/lib/insika/commands/backfill_knowledge.rb +145 -0
  71. data/lib/insika/commands/cancel_followup.rb +49 -0
  72. data/lib/insika/commands/delete_artifact.rb +35 -0
  73. data/lib/insika/commands/delete_concept.rb +34 -0
  74. data/lib/insika/commands/delete_mcp.rb +6 -2
  75. data/lib/insika/commands/delete_tenant_data.rb +107 -0
  76. data/lib/insika/commands/export_customer_memory.rb +48 -0
  77. data/lib/insika/commands/forget_customer.rb +117 -0
  78. data/lib/insika/commands/freeze_funnel_baseline.rb +113 -0
  79. data/lib/insika/commands/gate_harvest.rb +138 -0
  80. data/lib/insika/commands/gate_refinement.rb +1 -1
  81. data/lib/insika/commands/judge_shadow_pairs.rb +124 -0
  82. data/lib/insika/commands/memory_forget_fact.rb +20 -4
  83. data/lib/insika/commands/memory_put_fact.rb +23 -4
  84. data/lib/insika/commands/promote_harvest.rb +130 -0
  85. data/lib/insika/commands/record_outcome.rb +46 -0
  86. data/lib/insika/commands/record_shadow_reply.rb +68 -0
  87. data/lib/insika/commands/refresh_mcp_tools.rb +47 -0
  88. data/lib/insika/commands/reject_harvest.rb +38 -0
  89. data/lib/insika/commands/resolve_proposal.rb +108 -0
  90. data/lib/insika/commands/restore_concept.rb +34 -0
  91. data/lib/insika/commands/revoke_contact.rb +49 -0
  92. data/lib/insika/commands/rollback_harvest.rb +86 -0
  93. data/lib/insika/commands/run_distillation.rb +186 -0
  94. data/lib/insika/commands/run_harvest.rb +393 -0
  95. data/lib/insika/commands/seed_demo_data.rb +31 -0
  96. data/lib/insika/commands/send_message.rb +103 -6
  97. data/lib/insika/commands/session_purge.rb +67 -0
  98. data/lib/insika/commands/upsert_mcp.rb +6 -3
  99. data/lib/insika/commands/write_concept.rb +57 -0
  100. data/lib/insika/contact_store.rb +183 -0
  101. data/lib/insika/context/builder.rb +21 -3
  102. data/lib/insika/context/fragment.rb +7 -3
  103. data/lib/insika/context/priority.rb +5 -0
  104. data/lib/insika/context/provider.rb +17 -3
  105. data/lib/insika/context/providers/briefing.rb +96 -0
  106. data/lib/insika/context/providers/knowledge.rb +108 -0
  107. data/lib/insika/context/providers/memory.rb +16 -7
  108. data/lib/insika/context/providers/prompt.rb +57 -23
  109. data/lib/insika/context/providers/skill.rb +2 -0
  110. data/lib/insika/context/providers/tool_search.rb +2 -0
  111. data/lib/insika/context_trace_store.rb +38 -2
  112. data/lib/insika/cron.rb +189 -0
  113. data/lib/insika/demo/agent_attrs.rb +43 -0
  114. data/lib/insika/demo/golden_cases.rb +81 -0
  115. data/lib/insika/demo/seeder.rb +336 -0
  116. data/lib/insika/distill.rb +224 -0
  117. data/lib/insika/distill_engine.rb +169 -0
  118. data/lib/insika/doctor.rb +889 -11
  119. data/lib/insika/dsl/definition.rb +3 -2
  120. data/lib/insika/dsl/runtime.rb +69 -82
  121. data/lib/insika/dsl/server_boot.rb +92 -1
  122. data/lib/insika/dsl/system.rb +10 -2
  123. data/lib/insika/dsl.rb +186 -5
  124. data/lib/insika/edge_limiter.rb +28 -7
  125. data/lib/insika/env_schema.rb +25 -3
  126. data/lib/insika/errors.rb +11 -0
  127. data/lib/insika/evals/assertions.rb +3 -2
  128. data/lib/insika/evals/golden.rb +41 -4
  129. data/lib/insika/evals/judge.rb +47 -2
  130. data/lib/insika/evals/pairwise.rb +29 -6
  131. data/lib/insika/evals/persona.rb +98 -0
  132. data/lib/insika/evals/runner.rb +9 -0
  133. data/lib/insika/evals/simulator.rb +225 -0
  134. data/lib/insika/evals/transport.rb +83 -1
  135. data/lib/insika/event_stream.rb +10 -0
  136. data/lib/insika/evidence.rb +183 -0
  137. data/lib/insika/executor.rb +869 -76
  138. data/lib/insika/followup_engine.rb +207 -0
  139. data/lib/insika/followup_policy.rb +198 -0
  140. data/lib/insika/followup_store.rb +306 -0
  141. data/lib/insika/funnel_declaration.rb +106 -0
  142. data/lib/insika/funnel_fold.rb +179 -0
  143. data/lib/insika/funnel_store.rb +163 -0
  144. data/lib/insika/golden_store.rb +17 -2
  145. data/lib/insika/grounding/matcher.rb +69 -0
  146. data/lib/insika/grounding.rb +44 -0
  147. data/lib/insika/harvest/conversion_gate.rb +159 -0
  148. data/lib/insika/harvest/criterion.rb +98 -0
  149. data/lib/insika/harvest/gate.rb +194 -0
  150. data/lib/insika/harvest/negative_list.rb +199 -0
  151. data/lib/insika/harvest.rb +241 -0
  152. data/lib/insika/harvest_engine.rb +193 -0
  153. data/lib/insika/harvest_store.rb +548 -0
  154. data/lib/insika/knowledge.rb +680 -0
  155. data/lib/insika/knowledge_store.rb +140 -0
  156. data/lib/insika/mcp_client.rb +94 -0
  157. data/lib/insika/mcp_json.rb +74 -0
  158. data/lib/insika/mcp_live_tool.rb +43 -0
  159. data/lib/insika/mcp_store.rb +98 -26
  160. data/lib/insika/mcp_tool_ingestor.rb +30 -8
  161. data/lib/insika/mcp_tool_registry.rb +100 -0
  162. data/lib/insika/media.rb +382 -0
  163. data/lib/insika/memory_audit_store.rb +85 -0
  164. data/lib/insika/memory_store.rb +264 -23
  165. data/lib/insika/message_origin.rb +7 -2
  166. data/lib/insika/middleware.rb +9 -0
  167. data/lib/insika/model_visible.rb +87 -0
  168. data/lib/insika/model_visible_trace_store.rb +66 -0
  169. data/lib/insika/onboarding.rb +22 -1
  170. data/lib/insika/outbox_store.rb +42 -4
  171. data/lib/insika/outcome_store.rb +147 -0
  172. data/lib/insika/overlay_tool_registry.rb +37 -17
  173. data/lib/insika/packaging.rb +163 -0
  174. data/lib/insika/parity/criterion.rb +79 -0
  175. data/lib/insika/parity/verdict.rb +318 -0
  176. data/lib/insika/prefix_fingerprint.rb +58 -0
  177. data/lib/insika/profile_source.rb +29 -1
  178. data/lib/insika/prompt_catalog.rb +10 -0
  179. data/lib/insika/proposal_store.rb +271 -0
  180. data/lib/insika/queue_policy.rb +4 -1
  181. data/lib/insika/refinement/proposer.rb +1 -1
  182. data/lib/insika/reliability.rb +32 -6
  183. data/lib/insika/retention.rb +316 -0
  184. data/lib/insika/router/app.rb +157 -0
  185. data/lib/insika/router/backend_pool.rb +98 -0
  186. data/lib/insika/router/hash_ring.rb +55 -0
  187. data/lib/insika/router/proxy_body.rb +34 -0
  188. data/lib/insika/router/session_key.rb +54 -0
  189. data/lib/insika/router.rb +18 -0
  190. data/lib/insika/routing.rb +101 -0
  191. data/lib/insika/safety/config.rb +44 -4
  192. data/lib/insika/safety/corpus.rb +255 -0
  193. data/lib/insika/safety/detectors.rb +32 -113
  194. data/lib/insika/safety/factory.rb +15 -2
  195. data/lib/insika/safety/grounding_enforcer.rb +59 -0
  196. data/lib/insika/safety/grounding_validator.rb +49 -0
  197. data/lib/insika/safety/input_guardrail.rb +1 -1
  198. data/lib/insika/safety/moderator.rb +1 -1
  199. data/lib/insika/safety/output_filter.rb +9 -5
  200. data/lib/insika/safety/output_validator.rb +11 -5
  201. data/lib/insika/schedule.rb +177 -0
  202. data/lib/insika/schedule_engine.rb +314 -0
  203. data/lib/insika/schedule_store.rb +208 -0
  204. data/lib/insika/schema_guard.rb +35 -0
  205. data/lib/insika/server/app.rb +247 -17
  206. data/lib/insika/server/rack_app.rb +21 -1
  207. data/lib/insika/server/responses.rb +40 -1
  208. data/lib/insika/session_actor.rb +8 -4
  209. data/lib/insika/session_store.rb +65 -2
  210. data/lib/insika/settings_store.rb +10 -0
  211. data/lib/insika/shadow_pair_store.rb +258 -0
  212. data/lib/insika/skill_catalog.rb +16 -0
  213. data/lib/insika/soak/envelope.rb +140 -0
  214. data/lib/insika/soak/report.rb +392 -0
  215. data/lib/insika/soak/runner.rb +554 -0
  216. data/lib/insika/steer_injector.rb +21 -10
  217. data/lib/insika/store.rb +10 -1
  218. data/lib/insika/stores/memory.rb +6 -0
  219. data/lib/insika/stores/sqlite.rb +8 -0
  220. data/lib/insika/studio/app.rb +1437 -54
  221. data/lib/insika/studio/assets/dist/application.css +1 -1
  222. data/lib/insika/studio/assets/dist/application.js +23 -23
  223. data/lib/insika/studio/assets/dist/favicon.svg +6 -0
  224. data/lib/insika/studio/forms.rb +308 -10
  225. data/lib/insika/studio/nav_icons.rb +19 -1
  226. data/lib/insika/studio/views/_agent_tab_cache.erb +25 -0
  227. data/lib/insika/studio/views/_agent_tab_config.erb +514 -0
  228. data/lib/insika/studio/views/_agent_tab_history.erb +24 -0
  229. data/lib/insika/studio/views/_agent_tab_loops.erb +54 -0
  230. data/lib/insika/studio/views/_agent_tab_memory.erb +51 -0
  231. data/lib/insika/studio/views/_agent_tab_outcomes.erb +31 -0
  232. data/lib/insika/studio/views/_agent_tab_prompts.erb +108 -0
  233. data/lib/insika/studio/views/_agent_tab_skills.erb +38 -0
  234. data/lib/insika/studio/views/_agents_master.erb +44 -0
  235. data/lib/insika/studio/views/_message.erb +49 -32
  236. data/lib/insika/studio/views/agent_detail.erb +62 -278
  237. data/lib/insika/studio/views/agents.erb +71 -54
  238. data/lib/insika/studio/views/approvals.erb +4 -1
  239. data/lib/insika/studio/views/artifact.erb +23 -0
  240. data/lib/insika/studio/views/artifacts.erb +59 -0
  241. data/lib/insika/studio/views/chats.erb +4 -1
  242. data/lib/insika/studio/views/customer.erb +94 -0
  243. data/lib/insika/studio/views/customers.erb +32 -0
  244. data/lib/insika/studio/views/evals.erb +6 -3
  245. data/lib/insika/studio/views/facts.erb +133 -0
  246. data/lib/insika/studio/views/followups.erb +125 -0
  247. data/lib/insika/studio/views/funnel.erb +106 -0
  248. data/lib/insika/studio/views/harvest.erb +234 -0
  249. data/lib/insika/studio/views/home.erb +106 -66
  250. data/lib/insika/studio/views/knowledge.erb +123 -0
  251. data/lib/insika/studio/views/layout.erb +15 -11
  252. data/lib/insika/studio/views/mcp.erb +174 -80
  253. data/lib/insika/studio/views/parity.erb +147 -0
  254. data/lib/insika/studio/views/playground.erb +7 -1
  255. data/lib/insika/studio/views/session.erb +233 -124
  256. data/lib/insika/studio/views/settings.erb +41 -1
  257. data/lib/insika/studio/views/skills.erb +1 -2
  258. data/lib/insika/studio/views/system_files.erb +1 -1
  259. data/lib/insika/studio/views/task.erb +13 -0
  260. data/lib/insika/studio/views/tasks.erb +4 -1
  261. data/lib/insika/studio/views/tools.erb +24 -10
  262. data/lib/insika/task_store.rb +21 -1
  263. data/lib/insika/templates/browser-agent/README.md +36 -0
  264. data/lib/insika/templates/browser-agent/agent.rb +49 -0
  265. data/lib/insika/templates/daily-digest/README.md +38 -0
  266. data/lib/insika/templates/daily-digest/agent.rb +77 -0
  267. data/lib/insika/templates/repo-explorer/README.md +36 -0
  268. data/lib/insika/templates/repo-explorer/agent.rb +45 -0
  269. data/lib/insika/templates/research-analyst/README.md +26 -0
  270. data/lib/insika/templates/research-analyst/agent.rb +58 -0
  271. data/lib/insika/templates/review-panel/README.md +20 -0
  272. data/lib/insika/templates/review-panel/agent.rb +50 -0
  273. data/lib/insika/templates/travel-planner/README.md +35 -0
  274. data/lib/insika/templates/travel-planner/agent.rb +87 -0
  275. data/lib/insika/templates.rb +112 -0
  276. data/lib/insika/testing/store_contract.rb +27 -6
  277. data/lib/insika/tick.rb +47 -11
  278. data/lib/insika/timezone.rb +45 -0
  279. data/lib/insika/tool_definition.rb +17 -7
  280. data/lib/insika/tool_envelope.rb +69 -0
  281. data/lib/insika/tool_manifest.rb +5 -1
  282. data/lib/insika/tools/data_defined_tool.rb +10 -0
  283. data/lib/insika/tools/generate_image.rb +89 -0
  284. data/lib/insika/tools/load_knowledge.rb +74 -0
  285. data/lib/insika/tools/run_persona_eval.rb +328 -0
  286. data/lib/insika/tools/save_artifact.rb +95 -0
  287. data/lib/insika/tools/schedule_followup.rb +164 -0
  288. data/lib/insika/tools/tts.rb +47 -0
  289. data/lib/insika/tools/update_briefing.rb +126 -0
  290. data/lib/insika/turn_output.rb +1 -1
  291. data/lib/insika/turn_state.rb +49 -1
  292. data/lib/insika/turn_timing.rb +22 -2
  293. data/lib/insika/version.rb +1 -1
  294. data/lib/insika/vitals.rb +84 -0
  295. data/lib/insika/wiring/graph.rb +472 -13
  296. data/lib/insika/wiring/graph_chat.rb +102 -0
  297. data/lib/insika.rb +149 -3
  298. metadata +181 -5
  299. data/docs/build.md +0 -14
  300. data/docs/understand.md +0 -10
@@ -0,0 +1,199 @@
1
+ // Insika's own styling on top of Just the Docs.
2
+ //
3
+ // Everything that is NOT one of the theme's `!default` variables lives here;
4
+ // the palette itself is in _sass/color_schemes/insika.scss. Kept deliberately
5
+ // small: the theme already handles layout, search and responsiveness, and every
6
+ // rule below exists because a specific page needed it.
7
+ //
8
+ // IMPORTANT: these pages are read in three places — this site, GitHub, and the
9
+ // raw-markdown API (`GET /docs/<name>.md`). Styling is therefore driven by
10
+ // kramdown attribute lists (`{: .card-grid }`) on ORDINARY markdown, never by
11
+ // raw HTML blocks: a list still reads as a list everywhere else.
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Typography
15
+ // ---------------------------------------------------------------------------
16
+
17
+ // The theme's 1.6 content line-height is tight for pages this long.
18
+ .main-content {
19
+ line-height: 1.65;
20
+
21
+ h1,
22
+ h2,
23
+ h3 {
24
+ letter-spacing: -0.01em;
25
+ }
26
+
27
+ // A rule above every h2 turns a long page into visible sections. h2 is the
28
+ // level the search index already uses as a landmark, so it is the honest one
29
+ // to draw.
30
+ h2 {
31
+ padding-top: $sp-5;
32
+ margin-top: $sp-7;
33
+ border-top: $border $border-color;
34
+ }
35
+
36
+ h2:first-of-type {
37
+ margin-top: $sp-4;
38
+ padding-top: 0;
39
+ border-top: 0;
40
+ }
41
+
42
+ // Inline code appears in nearly every sentence here (env vars, method names).
43
+ // A tinted chip separates it from prose without shouting.
44
+ p > code,
45
+ li > code,
46
+ td > code,
47
+ h2 > code,
48
+ h3 > code,
49
+ h4 > code {
50
+ padding: 0.12em 0.32em;
51
+ background-color: $stone-100;
52
+ border: $border $stone-200;
53
+ border-radius: 3px;
54
+ font-size: 0.85em;
55
+ }
56
+
57
+ blockquote {
58
+ margin-left: 0;
59
+ padding: $sp-2 $sp-4;
60
+ border-left: 3px solid $terracotta-000;
61
+ background-color: $stone-050;
62
+ color: $ink-700;
63
+
64
+ > :first-child { margin-top: 0; }
65
+ > :last-child { margin-bottom: 0; }
66
+ }
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Home hero
71
+ // ---------------------------------------------------------------------------
72
+
73
+ // `{: .hero-tagline }` on the one line under the h1.
74
+ .hero-tagline {
75
+ max-width: 34rem;
76
+ margin-bottom: $sp-6;
77
+ color: $ink-700;
78
+ font-size: $font-size-6;
79
+ font-weight: 300;
80
+ line-height: 1.45;
81
+ }
82
+
83
+ // ---------------------------------------------------------------------------
84
+ // Card grid
85
+ // ---------------------------------------------------------------------------
86
+
87
+ // `{: .card-grid }` on a markdown list whose items read
88
+ // `**[Title](page.md)** — what the page answers.`
89
+ //
90
+ // The em dash is the split point: the bold link becomes the card title and the
91
+ // remainder becomes the card's body. No HTML in the markdown, so GitHub and the
92
+ // raw-markdown API still see a plain, readable list.
93
+ .main-content ul.card-grid {
94
+ display: grid;
95
+ grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
96
+ gap: $sp-3;
97
+ margin: $sp-5 0;
98
+ padding: 0;
99
+ list-style: none;
100
+
101
+ > li {
102
+ margin: 0;
103
+ padding: $sp-4;
104
+ border: $border $border-color;
105
+ border-radius: 6px;
106
+ background-color: $body-background-color;
107
+ line-height: 1.5;
108
+ transition: border-color 150ms ease, background-color 150ms ease;
109
+
110
+ &::before { content: none; }
111
+
112
+ &:hover {
113
+ border-color: $terracotta-000;
114
+ background-color: $stone-050;
115
+ }
116
+
117
+ // The bold-wrapped link is the card's title.
118
+ > strong {
119
+ display: block;
120
+ margin-bottom: $sp-1;
121
+ font-size: $font-size-5;
122
+ font-weight: 600;
123
+
124
+ > a {
125
+ color: $body-heading-color;
126
+ text-decoration: none;
127
+ background-image: none;
128
+
129
+ &:hover { color: $terracotta-100; }
130
+ }
131
+ }
132
+
133
+ color: $ink-500;
134
+ font-size: $font-size-4;
135
+ }
136
+ }
137
+
138
+ // ---------------------------------------------------------------------------
139
+ // Navigation
140
+ // ---------------------------------------------------------------------------
141
+
142
+ .site-title {
143
+ font-weight: 600;
144
+ letter-spacing: -0.02em;
145
+ }
146
+
147
+ // The sidebar is six sections deep; a hairline between top-level entries makes
148
+ // the grouping legible at a glance instead of one 40-item column.
149
+ .site-nav > .nav-list > .nav-list-item + .nav-list-item {
150
+ border-top: $border $border-color;
151
+ }
152
+
153
+ .nav-list .nav-list-item .nav-list-link.active {
154
+ font-weight: 600;
155
+ box-shadow: inset 2px 0 0 $terracotta-100;
156
+ }
157
+
158
+ // ---------------------------------------------------------------------------
159
+ // Tables
160
+ // ---------------------------------------------------------------------------
161
+
162
+ // Reference tables here are wide (env var, default, meaning). Zebra striping
163
+ // and a tinted header make a 20-row table scannable.
164
+ .main-content table {
165
+ th {
166
+ background-color: $stone-100;
167
+ font-size: $font-size-3;
168
+ text-transform: uppercase;
169
+ letter-spacing: 0.04em;
170
+ }
171
+
172
+ tbody tr:nth-child(even) {
173
+ background-color: $stone-050;
174
+ }
175
+ }
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // Site title
179
+ // ---------------------------------------------------------------------------
180
+
181
+ // _includes/title.html puts the pillar mark inline before the wordmark.
182
+ .site-title {
183
+ display: inline-flex;
184
+ align-items: center;
185
+ gap: $sp-2;
186
+ }
187
+
188
+ .site-title-mark {
189
+ display: inline-flex;
190
+ flex: 0 0 auto;
191
+
192
+ svg {
193
+ width: 1.05em;
194
+ height: 1.05em;
195
+ fill: $terracotta-100;
196
+
197
+ .shaft { fill: $terracotta-000; }
198
+ }
199
+ }
@@ -0,0 +1,26 @@
1
+ // Insika's palette tokens.
2
+ //
3
+ // This file is the ONE place the raw colours are written. It lives in
4
+ // custom/setup rather than in the colour scheme because Just the Docs imports
5
+ // it before every scheme it compiles — including its own stock light and dark
6
+ // stylesheets, which also pull in _sass/custom/custom.scss and would otherwise
7
+ // fail on an undefined variable.
8
+ //
9
+ // _sass/color_schemes/insika.scss maps these onto the theme's own variables.
10
+
11
+ // Warm neutrals. The greys carry a little red so that text on the off-white
12
+ // sidebar does not read as blue-grey next to the terracotta accent.
13
+ $ink-900: #16191d !default; // headings
14
+ $ink-700: #3d4148 !default; // body copy
15
+ $ink-500: #6b6a68 !default; // muted
16
+ $stone-050: #faf8f5 !default; // sidebar
17
+ $stone-100: #f6f3ef !default; // code blocks, table stripes
18
+ $stone-200: #e8e2d9 !default; // borders
19
+
20
+ // The single accent. #a8431e clears 4.5:1 on both #fff and the sidebar, which
21
+ // the lighter, prettier terracottas do not — links are the one thing here that
22
+ // cannot trade contrast for warmth.
23
+ $terracotta-000: #c2542a !default;
24
+ $terracotta-100: #a8431e !default;
25
+ $terracotta-200: #8a3617 !default;
26
+ $terracotta-300: #6b2911 !default;
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-label="Insika">
2
+ <!-- A pillar seen head-on: capital, shaft, base. It doubles as the "I" of
3
+ Insika, which is why the shaft is centred and the slabs overhang. -->
4
+ <rect x="3" y="3" width="18" height="3.6" rx="1" fill="#a8431e"/>
5
+ <rect x="8.7" y="7.6" width="6.6" height="8.8" fill="#c2542a"/>
6
+ <rect x="3" y="17.4" width="18" height="3.6" rx="1" fill="#a8431e"/>
7
+ </svg>
@@ -0,0 +1,7 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-label="Insika">
2
+ <!-- A pillar seen head-on: capital, shaft, base. It doubles as the "I" of
3
+ Insika, which is why the shaft is centred and the slabs overhang. -->
4
+ <rect x="3" y="3" width="18" height="3.6" rx="1" fill="#a8431e"/>
5
+ <rect x="8.7" y="7.6" width="6.6" height="8.8" fill="#c2542a"/>
6
+ <rect x="3" y="17.4" width="18" height="3.6" rx="1" fill="#a8431e"/>
7
+ </svg>
@@ -0,0 +1,21 @@
1
+ ---
2
+ title: Core concepts
3
+ nav_order: 3
4
+ has_children: true
5
+ permalink: /core-concepts/
6
+ ---
7
+
8
+ # Core concepts
9
+
10
+ An agent is data: a profile, what it is allowed to do, its tools, its skills, and
11
+ what fills its prompt. These six pages are the vocabulary everything else on this
12
+ site assumes. Each one has a runnable counterpart under
13
+ [`examples/`](https://github.com/guizaols/insika/tree/main/examples/).
14
+
15
+ - **[Agents](AGENTS.md)** — the profile, the three ways to create one, and every key on it.
16
+ - **[Limits and policy](POLICY.md)** — the five layers that decide what an agent may do and what stops it.
17
+ - **[Tools](TOOLS.md)** — code tools, data-defined tools, MCP servers, and why a tool call goes missing.
18
+ - **[Skills](SKILLS.md)** — playbooks the agent loads only when the conversation calls for them.
19
+ - **[Context](CONTEXT.md)** — what fills a turn's prompt, the token budget, and cross-session memory.
20
+ - **[Workflows](WORKFLOWS.md)** — when the order of work belongs in Ruby instead of a prompt.
21
+ {: .card-grid }
data/docs/domain.md ADDED
@@ -0,0 +1,115 @@
1
+ ---
2
+ title: The domain-free core
3
+ parent: Reference
4
+ nav_order: 1
5
+ permalink: /domain/
6
+ ---
7
+
8
+ # The domain-free core — what ships, what a deployment declares, and how to clear it
9
+
10
+ The engine is domain-free by construction: the gem carries no store
11
+ vocabulary, no persona, and no fixed conversation language. This page is the
12
+ removability map — for every artifact that could make a deployment look like
13
+ "the Brazilian store harness", here is what ships, what the doctor reports, and
14
+ how to clear it.
15
+
16
+ ## What ships and what does not
17
+
18
+ One selection function owns the gem payload: `Insika::Packaging.payload_files`
19
+ — the gemspec and the audit spec (`spec/insika/domain_boundary_spec.rb`) call
20
+ the same function, so the boundary is a fact the suite asserts on, never a
21
+ prose promise. It ships:
22
+
23
+ - `lib/` — the engine, the server, the Studio (the compiled JS only);
24
+ - `docs/` — the public documentation;
25
+ - `README.md`, `LICENSE`, `CHANGELOG.md`, `bin/insika`.
26
+
27
+ It never ships: `deploy/`, `packs/`, `examples/`, `plugins/`, `evals/`,
28
+ `scripts/`, `spec/` — even a tracked pack cannot ship. Packs, personas and
29
+ deployment wiring are checkout material, and that is the point: the reference
30
+ deployment is not the gem.
31
+
32
+ The proof command is `insika doctor --domain`: a read-only inventory of what a
33
+ deployment declares, plus the built-in corpora still in effect. A bare install
34
+ with no agents names nothing; an install that boots an agent reports the
35
+ built-in pt-BR guardrail corpus as `source: gem-default` — the removability
36
+ surface, not a store. The section never fails the doctor's exit code: it is
37
+ informational, the RFC's proof surface.
38
+
39
+ ## The guardrail corpus — clear the shipped pt-BR content
40
+
41
+ The deterministic guardrail patterns ship as language-tagged data
42
+ (`Safety::Corpus`). A deployment clears a language by config, per agent:
43
+
44
+ ```jsonc
45
+ // pack: agent.config.json
46
+ { "guardrails": { "corpora": { "languages": ["en"] } } }
47
+ ```
48
+
49
+ ```ruby
50
+ # DSL
51
+ agent = Insika.agent("store-support") do
52
+ instructions "…"
53
+ guardrails corpora: { languages: ["en"] }
54
+ end
55
+ ```
56
+
57
+ - `languages` filters the shipped families: `nil` = all (the default),
58
+ `[]` = none, `["en"]` = the EN-only corpus. An EN-only deployment drops the
59
+ pt-BR input heuristics **and** the CPF/CNPJ output redaction — a documented
60
+ consequence: the tax-id formats are pt-BR data, and the universal "secret"
61
+ redaction is never cleared.
62
+ - `extra` adds source-string patterns per family:
63
+
64
+ ```jsonc
65
+ { "guardrails": { "corpora": { "extra": { "abuse": ["/\\bdupa\\b/i"] } } } }
66
+ ```
67
+
68
+ - `guardrails.responses` replaces the built-in pt-BR fallback replies (the
69
+ safe refusals). Set a `default` to clear every category at once.
70
+
71
+ See [Security](SECURITY.md) for the guardrail layers; the doctor's domain
72
+ section enumerates any agent still running the built-in pt-BR corpus or the
73
+ built-in replies, with the clear path in each entry. An unknown language or
74
+ family, or a malformed pattern, is refused at boot — `insika doctor` reports it
75
+ as an error and the agent never turns with a broken corpus.
76
+
77
+ ## Marking a deployment — declare, never infer
78
+
79
+ The engine never guesses a store. Domain markers are data a deployment
80
+ declares, and the doctor reads only those declarations:
81
+
82
+ - **Personas/packs** — `metadata.domain` on the agent profile:
83
+
84
+ ```jsonc
85
+ { "metadata": { "domain": "e-commerce-pt-BR" } }
86
+ ```
87
+
88
+ ```ruby
89
+ metadata domain: "e-commerce-pt-BR"
90
+ ```
91
+
92
+ - **Outcome funnel** — `funnel:` on the profile (see
93
+ [Outcomes](OUTCOMES.md#outcomes--business-results-over-real-traffic)).
94
+ Vocabulary note: in the gem and the doctor output it is an **outcome
95
+ funnel**, never "conversion" — the stage names are the deployment's, and a
96
+ bare install shows no funnel and no stage names at all.
97
+ - **Evidence** — the `evidence:` declaration on a tool manifest (see
98
+ [Tools](TOOLS.md)); the kinds are the deployment's vocabulary, never gem
99
+ constants.
100
+
101
+ `insika doctor --domain` enumerates all four with their source —
102
+ `deployment` for declared data, `gem-default` for the built-in corpus still in
103
+ effect — and a bare boot names none.
104
+
105
+ ## The conformance claim — model-visible means logged
106
+
107
+ "Every byte that reaches the provider is reconstructable from checkpoints +
108
+ traces" is a spec, not a promise. For each (task, turn) the engine records the
109
+ model-visible payload at the provider boundary — the system text, the tool
110
+ schemas, and the full message stream (`ModelVisibleTraceStore`) — next to the
111
+ durable transcript (the checkpoint). The conformance suite
112
+ (`spec/insika/conformance/model_visible_spec.rb`) drives real turns on a
113
+ capturing chat and asserts a three-way byte identity: what the chat held ==
114
+ the checkpoint transcript == the model trace. A path whose bytes are not
115
+ logged is a fix in the engine, never a waiver in the suite.
data/docs/improve.md ADDED
@@ -0,0 +1,20 @@
1
+ ---
2
+ title: Improve
3
+ nav_order: 7
4
+ has_children: true
5
+ permalink: /improve/
6
+ ---
7
+
8
+ # Improve
9
+
10
+ The loops that make an agent better than it was last month — measure it, read
11
+ its own traffic back, and turn finished conversations into something the next
12
+ conversation can use. Every one of them ends at a human approval.
13
+
14
+ - **[Evals](EVALS.md)** — the cases that grade an agent, and the gate that stops a regression.
15
+ - **[Refinement](REFINEMENT.md)** — production traffic read back as a ranked report of what broke.
16
+ - **[Outcomes and follow-ups](OUTCOMES.md)** — what the traffic was worth, and coming back on a promise.
17
+ - **[Knowledge](KNOWLEDGE.md)** — durable concepts extracted from finished conversations.
18
+ - **[Facts](FACTS.md)** — distilled customer memory, approved one fact at a time.
19
+ - **[Harvest](HARVEST.md)** — skills mined from real traffic, promoted only if two gates hold.
20
+ {: .card-grid }
data/docs/index.md CHANGED
@@ -8,7 +8,7 @@ permalink: /
8
8
  {: .fs-9 }
9
9
 
10
10
  Your agent is the idea. Insika is what holds it up in production.
11
- {: .fs-6 .fw-300 }
11
+ {: .hero-tagline }
12
12
 
13
13
  [Build your first agent](RUNNING-LOCAL.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
14
14
  [View on GitHub](https://github.com/guizaols/insika){: .btn .fs-5 .mb-4 .mb-md-0 }
@@ -36,7 +36,7 @@ require "insika"
36
36
  assistant = Insika.agent("assistant") do
37
37
  model "deepseek-v4-flash"
38
38
  provider :deepseek
39
- instructions "You are Bia, a concise and friendly assistant. Answer briefly."
39
+ instructions "You are a concise and friendly assistant. Answer briefly."
40
40
  end
41
41
 
42
42
  puts assistant.reply("hi, what can you do?") # one turn, in-process
@@ -60,9 +60,12 @@ secrets), `GET /docs` and `GET /docs/<name>.md`. Public and on by default when y
60
60
 
61
61
  ## Where to go next
62
62
 
63
- - **[Understand the idea](understand.md)** — why a runtime rather than a DIY loop, and how a turn actually runs.
64
- - **[Build an agent](build.md)** — agents, tools, skills, context, and the local loop.
65
- - **[Ship it](ship.md)** — security, confined execution, deployment.
66
- - **[Operate & prove it](operate.md)** — observability, the benchmark, load testing, evals, refinement.
63
+ - **[Start here](start-here.md)** — why a runtime, getting one running, and what a turn actually does.
64
+ - **[Core concepts](core-concepts.md)** — agents, limits, tools, skills, context, workflows.
65
+ - **[Integrate](integrate.md)** — the API, channels, media, embedding, plugins, templates.
66
+ - **[Ship it](ship.md)** — security, confined execution, deployment, scaling past one worker.
67
+ - **[Operate](operate.md)** — observability, schedules, artifacts, load and soak testing.
68
+ - **[Improve](improve.md)** — evals, refinement, outcomes, and the three learning loops.
69
+ {: .card-grid }
67
70
 
68
71
  Pre-release: APIs may still change and nothing is tagged yet. Licensed MIT.
data/docs/integrate.md ADDED
@@ -0,0 +1,20 @@
1
+ ---
2
+ title: Integrate
3
+ nav_order: 4
4
+ has_children: true
5
+ permalink: /integrate/
6
+ ---
7
+
8
+ # Integrate
9
+
10
+ How the agent connects to everything that is not the engine: the clients that
11
+ call it, the places people talk to it from, the app it may live inside, and the
12
+ code you write to extend it.
13
+
14
+ - **[The /v1 API](API.md)** — the frozen, OpenAI-Responses-compatible contract.
15
+ - **[Channels](CHANNELS.md)** — the web widget and the relay: how people actually reach the agent.
16
+ - **[Media](MEDIA.md)** — photos, voice notes and documents in; generated images out.
17
+ - **[Embedding](EMBEDDING.md)** — mounting the engine inside a Ruby app you already have.
18
+ - **[Plugins](PLUGINS.md)** — the two extension tiers, and how to pick between them.
19
+ - **[Templates](TEMPLATES.md)** — the example agents that ship in the gem, and the gallery that installs them.
20
+ {: .card-grid }
data/docs/operate.md CHANGED
@@ -1,12 +1,19 @@
1
1
  ---
2
- title: Operate & prove it
3
- nav_order: 5
2
+ title: Operate
3
+ nav_order: 6
4
4
  has_children: true
5
5
  permalink: /operate/
6
6
  ---
7
7
 
8
- # Operate & prove it
8
+ # Operate
9
9
 
10
- Turns as traces and metrics, the engine's measured overhead, how to load-test it
11
- yourself, the cases that grade an agent, and reading a live agent's own traffic back as
12
- a report of what broke.
10
+ Running it once it is live: seeing what happened, firing turns nobody sent,
11
+ handing back a report, and knowing what the box will take before it takes it.
12
+
13
+ - **[Observability](OBSERVABILITY.md)** — the event stream, and turns as OpenTelemetry traces and metrics.
14
+ - **[Schedules](SCHEDULING.md)** — recurring turns the engine fires on its own tick.
15
+ - **[Artifacts](ARTIFACTS.md)** — a report the agent can hand you a URL to.
16
+ - **[Load test](LOADTEST.md)** — end-to-end load testing, and comparing topologies before you choose one.
17
+ - **[Soak](SOAK.md)** — the long run that finds what a short one cannot.
18
+ - **[Benchmark](BENCHMARK.md)** — the engine's measured overhead, reproducible without an API key.
19
+ {: .card-grid }
@@ -0,0 +1,118 @@
1
+ ---
2
+ title: Prompt — add a tool or skill
3
+ parent: Reference
4
+ nav_order: 3
5
+ permalink: /add-tool/
6
+ layout: default
7
+ render_with_liquid: false
8
+ ---
9
+
10
+ # Add a tool, MCP server, or skill to my agent
11
+
12
+ > **You are a coding agent** (Claude Code, Codex, Cursor, …) reading this because a
13
+ > developer pasted a prompt pointing here. Treat this file as a **skill**: follow the
14
+ > steps in order and apply the RULES literally.
15
+
16
+ Your job: extend **one existing agent** with **one new capability** and prove it works
17
+ with one turn. Nothing more.
18
+
19
+ ## Step 0 — Gather context (silently)
20
+
21
+ - **Which agent?** Find its `Insika.agent { … }` block (or its pack). If the developer
22
+ has no agent yet, stop and build the minimal first agent instead — this file assumes
23
+ one exists. Do not mix onboarding with capability.
24
+ - **A provider key in the environment**; ask if none is set.
25
+ - Read [`docs/TOOLS.md`](../TOOLS.md) (also served at `GET /docs/tools.md`) and, for
26
+ skills, [`docs/SKILLS.md`](../SKILLS.md) before writing anything.
27
+
28
+ ## Step 1 — Pick the kind (RULES, not taste)
29
+
30
+ | The need | The kind | Where it lives |
31
+ |---|---|---|
32
+ | Call an external HTTP API | **data tool** (`data_tool` in the DSL block) | a row in SQLite, editable at runtime |
33
+ | Logic must run in-process | **code tool** (a Ruby class `< RubyLLM::Tool`) | the deployment image |
34
+ | Adopt a whole external MCP server | **`mcp` instance** | durable config; its tools appear tagged `mcp:<name>` |
35
+ | Teach a procedure (no data fetching) | **skill** (`skill "name", description:, instructions:`) | loads on demand via `load_skill` |
36
+
37
+ Exactly one kind. A skill is not a tool; an MCP server is not five data tools.
38
+
39
+ ## Step 2 — Build the smallest version
40
+
41
+ Data tool, via DSL (shape from
42
+ [`examples/data-tool/`](https://github.com/guizaols/insika/tree/main/examples/data-tool/)):
43
+
44
+ ```ruby
45
+ data_tool(
46
+ "name" => "convert_currency",
47
+ "description" => "Latest reference exchange rate between two currencies.",
48
+ "parameters" => {
49
+ "type" => "object",
50
+ "properties" => {
51
+ "from" => { "type" => "string", "description" => "source currency code" },
52
+ "to" => { "type" => "string", "description" => "target currency code" }
53
+ },
54
+ "required" => %w[from to]
55
+ },
56
+ "request" => { "method" => "GET",
57
+ "url" => "https://api.example.com/latest?from={{from}}&to={{to}}" },
58
+ "response" => { "extract" => "body_raw" }
59
+ )
60
+ ```
61
+
62
+ Skill, via DSL (from
63
+ [`examples/skills/`](https://github.com/guizaols/insika/tree/main/examples/skills/)):
64
+
65
+ ```ruby
66
+ skill "refunds",
67
+ description: "How to handle a refund request",
68
+ instructions: <<~MD
69
+ When a customer asks for a refund:
70
+ 1. If you don't have the order number, ask for it first.
71
+ …
72
+ MD
73
+ ```
74
+
75
+ RULES:
76
+
77
+ - `parameters` is JSON Schema (safe subset — no `oneOf`/`$ref`); it reaches the model
78
+ verbatim and arguments are checked against it at call time.
79
+ - Author the FINAL url: the HTTP client does not follow redirects, and the egress guard
80
+ cleared that host only.
81
+ - Do not add a second capability "while we're here".
82
+
83
+ ## Step 3 — Make sure it enters the tool-loop
84
+
85
+ Registered is not enough — the agent's policy allowlist decides. The DSL auto-enables
86
+ the allowlist policy, and the three-state rule applies (`nil` = all, `[]` = none,
87
+ `[names]` = exactly those; deny wins):
88
+
89
+ ```ruby
90
+ tools %w[convert_currency] # or tools_allow: [...] on the pack
91
+ ```
92
+
93
+ A code tool can never be shadowed by a data tool of the same name — pick another name
94
+ instead of fighting it.
95
+
96
+ ## Step 4 — Prove it with ONE turn
97
+
98
+ Run one `reply()` whose message forces the call ("how many BRL is 1 USD right now?").
99
+ The reply must use what the tool returned — if the model answers from imagination, the
100
+ tool did not run: re-check Step 3 before touching the prompt.
101
+
102
+ ## Step 5 — Self-check
103
+
104
+ - [ ] One agent, one new capability, one proving turn with real output.
105
+ - [ ] The tool/skill is named in the allowlist (or absence was a deliberate "all").
106
+ - [ ] No secret in any file — keys live in the environment.
107
+ - [ ] No new gem dependency was added without asking.
108
+
109
+ ## Hard constraints
110
+
111
+ - **Secrets stay in the environment.** `{{secret.*}}` resolves ONLY on the manifest
112
+ write path (`POST /v1/tools/manifest`); written via DSL or Studio it fails
113
+ registration. Tools authored outside a manifest ship literal values (masked on read).
114
+ - **The egress guard refusing a URL is a feature**, not a bug to disable globally.
115
+ Report it; open an allowlist exception deliberately.
116
+ - **Config over code**: everything above is data the DSL generates (`to_pack`). If it
117
+ seems to require reaching past the DSL, the answer is a DSL method you have not used
118
+ yet — re-read the docs first.
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: Prompt — diagnose a failed turn
3
+ parent: Reference
4
+ nav_order: 4
5
+ permalink: /diagnose-turn/
6
+ layout: default
7
+ render_with_liquid: false
8
+ ---
9
+
10
+ # Diagnose a turn that failed or misbehaved
11
+
12
+ > **You are a coding agent** (Claude Code, Codex, Cursor, …) reading this because a
13
+ > developer pasted a prompt pointing here — something like *"the agent didn't answer /
14
+ > answered wrong / errored"*. Treat this file as a **skill**: investigate BEFORE
15
+ > proposing fixes, and report findings in plain language with evidence.
16
+
17
+ The engine already recorded what happened: every turn emits structured events stamped
18
+ with `task_id`/`session_id`. Your job is to read the record, not to guess.
19
+
20
+ ## Step 0 — Pin down the facts
21
+
22
+ Ask for (or find) the minimum: **agent id**, **session id** (or the customer's message
23
+ text), roughly **when**, and expected vs. actual. Reproduce once locally if cheap
24
+ (`reply()` or one `curl` against a dev instance) — never hammer production.
25
+
26
+ ## Step 1 — Read the record, in this order
27
+
28
+ 1. **`GET /v1/tasks/:id`** (or the Studio) — the terminal state:
29
+ `completed` / `failed` / `cancelled`, outcome, usage, timing. No task id? Find it
30
+ via **`GET /v1/events?session_id=…`**.
31
+ 2. **That task's events**: `task_started` → `tool_call`/`tool_result`/`data_tool_call`
32
+ … → the terminal event. A failure's reason lives there.
33
+ 3. **`GET /v1/sessions/:id`** — the transcript: what the model actually saw and said.
34
+ 4. **`bin/insika doctor`** — configuration sanity; relay its findings verbatim.
35
+
36
+ ## Step 2 — Map symptom to mechanism
37
+
38
+ | On the record | Usual suspect | Details |
39
+ |---|---|---|
40
+ | provider auth/model error | missing key or wrong model id | fails at the provider, not the engine |
41
+ | `provider_failure` then `provider_fallback` | reliability chain rotated mid-turn | [Agents](../AGENTS.md) § reliability |
42
+ | `breaker_open` + fail-fast turns | circuit open until cooldown | same |
43
+ | turn completed, customer got nothing | delivery is separate from the turn: check `channel_delivered` / `delivery_failed` | [Channels](../CHANNELS.md) |
44
+ | freshly created agent returns empty turns | persona overflows the default `context_budget` (8000) | [Context](../CONTEXT.md) |
45
+ | tool never called (or "missing") | not registered OR not allowed (`tools_allow`) | [Tools](../TOOLS.md) § troubleshooting |
46
+ | identical `tool_call` repeated, then abort | the `max_tool_repeat` loop guard | [Agents](../AGENTS.md) § limits |
47
+ | model gave up after one empty result | `tool_persistence` off (it is ON by default) | same |
48
+ | `turn_stuck` event | the agent declared it cannot proceed — escalation signal, not a bug | [Agents](../AGENTS.md) § stuck |
49
+
50
+ ## Step 3 — Report, then fix ONE thing
51
+
52
+ - Plain-language summary: **what happened → evidence (event names + ids) → root cause
53
+ → the fix you propose.**
54
+ - Apply the fix; re-run the Step 0 reproduction; show the new terminal event as proof.
55
+ - If the evidence does not fit any row above, say so and bring the raw events back —
56
+ do not force a diagnosis.
57
+
58
+ ## Hard constraints
59
+
60
+ - **Never invent event data.** If you did not read it, you cannot claim it.
61
+ - **Do not change config just to silence the symptom** without explaining the
62
+ mechanism (raising `context_budget` because the prompt is big is a fix; deleting the
63
+ guardrail that fired is not).
64
+ - **Quote ids, counts and states first; message content only when needed** for the
65
+ developer to recognize the case.