@tech-leads-club/harness-toolkit 0.2.1

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. package/CHANGELOG.md +95 -0
  2. package/LICENSE +84 -0
  3. package/NOTICE +9 -0
  4. package/README.md +480 -0
  5. package/bin/tlc +10 -0
  6. package/bin/tlc-build +80 -0
  7. package/bin/tlc-cli.ts +1509 -0
  8. package/bin/tlc-exec +10 -0
  9. package/bin/tlc-exec.cmd +4 -0
  10. package/bin/tlc-exec.d.mts +60 -0
  11. package/bin/tlc-exec.mjs +248 -0
  12. package/bin/tlc.cmd +4 -0
  13. package/bin/tlc.mjs +7 -0
  14. package/bin/write-user-hooks.d.mts +40 -0
  15. package/bin/write-user-hooks.mjs +123 -0
  16. package/capabilities/catalog.json +418 -0
  17. package/config.example.json +51 -0
  18. package/dist/compact-before.mjs +7839 -0
  19. package/dist/doctor.mjs +8263 -0
  20. package/dist/help-topic.mjs +82 -0
  21. package/dist/init-project.mjs +993 -0
  22. package/dist/install-runtime.mjs +969 -0
  23. package/dist/lessons-cli.mjs +7258 -0
  24. package/dist/obs-cli.mjs +7025 -0
  25. package/dist/price-lookup.mjs +221 -0
  26. package/dist/prompt-submit.mjs +7860 -0
  27. package/dist/refresh-model-prices.mjs +158 -0
  28. package/dist/response-after.mjs +7879 -0
  29. package/dist/run.mjs +7830 -0
  30. package/dist/session-end.mjs +7937 -0
  31. package/dist/session-start.mjs +8056 -0
  32. package/dist/shim.mjs +52 -0
  33. package/dist/stop.mjs +8494 -0
  34. package/dist/subagent-start.mjs +7861 -0
  35. package/dist/subagent-stop.mjs +7851 -0
  36. package/dist/support.mjs +7045 -0
  37. package/dist/tlc-cli.mjs +8063 -0
  38. package/dist/tool-after.mjs +8113 -0
  39. package/dist/tool-before.mjs +7985 -0
  40. package/dist/tool-failure.mjs +7850 -0
  41. package/dist/uninstall-runtime.mjs +1307 -0
  42. package/docs/architecture.md +270 -0
  43. package/docs/concepts.md +562 -0
  44. package/docs/coverage.md +55 -0
  45. package/docs/decisions/ad-001.md +32 -0
  46. package/docs/decisions/ad-002.md +51 -0
  47. package/docs/decisions/ad-003.md +30 -0
  48. package/docs/decisions/ad-004.md +37 -0
  49. package/docs/decisions/ad-005.md +36 -0
  50. package/docs/decisions/ad-006.md +49 -0
  51. package/docs/decisions/ad-007.md +36 -0
  52. package/docs/decisions/ad-008.md +54 -0
  53. package/docs/decisions/ad-009.md +61 -0
  54. package/docs/decisions/ad-010.md +45 -0
  55. package/docs/decisions/ad-011.md +59 -0
  56. package/docs/decisions/ad-012.md +71 -0
  57. package/docs/decisions/ad-013.md +87 -0
  58. package/docs/decisions/ad-014.md +56 -0
  59. package/docs/decisions/ad-015.md +33 -0
  60. package/docs/decisions/ad-016.md +98 -0
  61. package/docs/decisions/ad-017.md +65 -0
  62. package/docs/decisions/ad-018.md +77 -0
  63. package/docs/decisions/ad-019.md +75 -0
  64. package/docs/decisions/ad-020.md +88 -0
  65. package/docs/decisions/ad-021.md +57 -0
  66. package/docs/decisions/ad-022.md +120 -0
  67. package/docs/decisions/ad-023.md +87 -0
  68. package/docs/decisions/ad-024.md +85 -0
  69. package/docs/decisions/ad-025.md +92 -0
  70. package/docs/decisions/ad-026.md +131 -0
  71. package/docs/decisions/ad-027.md +90 -0
  72. package/docs/decisions/ad-028.md +90 -0
  73. package/docs/decisions/ad-029.md +73 -0
  74. package/docs/decisions/ad-030.md +97 -0
  75. package/docs/decisions/ad-031.md +94 -0
  76. package/docs/decisions/ad-032.md +83 -0
  77. package/docs/decisions/ad-033.md +89 -0
  78. package/docs/decisions/ad-034.md +86 -0
  79. package/docs/decisions/ad-035.md +86 -0
  80. package/docs/decisions/ad-036.md +68 -0
  81. package/docs/decisions/ad-037.md +47 -0
  82. package/docs/decisions/ad-038.md +52 -0
  83. package/docs/decisions/ad-039.md +69 -0
  84. package/docs/decisions/ad-040.md +89 -0
  85. package/docs/decisions/ad-041.md +98 -0
  86. package/docs/decisions/ad-042.md +82 -0
  87. package/docs/decisions/ad-043.md +79 -0
  88. package/docs/decisions/ad-044.md +61 -0
  89. package/docs/decisions/ad-045.md +94 -0
  90. package/docs/decisions/ad-046.md +111 -0
  91. package/docs/decisions/ad-047.md +96 -0
  92. package/docs/decisions/ad-048.md +85 -0
  93. package/docs/decisions/ad-049.md +66 -0
  94. package/docs/decisions/ad-050.md +94 -0
  95. package/docs/decisions/ad-051.md +69 -0
  96. package/docs/decisions/ad-052.md +69 -0
  97. package/docs/decisions/ad-053.md +78 -0
  98. package/docs/decisions/ad-054.md +98 -0
  99. package/docs/decisions/ad-055.md +74 -0
  100. package/docs/decisions/ad-056.md +85 -0
  101. package/docs/decisions/ad-057.md +68 -0
  102. package/docs/decisions/ad-058.md +97 -0
  103. package/docs/decisions/ad-059.md +82 -0
  104. package/docs/decisions/ad-060.md +75 -0
  105. package/docs/decisions/ad-061.md +68 -0
  106. package/docs/decisions/ad-062.md +72 -0
  107. package/docs/decisions/ad-063.md +84 -0
  108. package/docs/decisions/ad-064.md +79 -0
  109. package/docs/decisions/ad-065.md +81 -0
  110. package/docs/decisions/ad-066.md +111 -0
  111. package/docs/decisions/ad-067.md +64 -0
  112. package/docs/decisions/ad-068.md +79 -0
  113. package/docs/decisions/ad-069.md +74 -0
  114. package/docs/decisions/ad-070.md +86 -0
  115. package/docs/decisions/ad-071.md +93 -0
  116. package/docs/decisions/ad-072.md +82 -0
  117. package/docs/decisions/ad-073.md +102 -0
  118. package/docs/decisions/ad-074.md +91 -0
  119. package/docs/decisions/ad-075.md +79 -0
  120. package/docs/decisions/ad-076.md +102 -0
  121. package/docs/decisions/ad-077.md +94 -0
  122. package/docs/decisions/ad-078.md +84 -0
  123. package/docs/decisions/ad-079.md +73 -0
  124. package/docs/decisions/ad-080.md +86 -0
  125. package/docs/decisions/ad-081.md +70 -0
  126. package/docs/decisions/ad-082.md +79 -0
  127. package/docs/decisions/ad-083.md +88 -0
  128. package/docs/decisions/index.md +111 -0
  129. package/docs/diagnose.md +248 -0
  130. package/docs/index.md +52 -0
  131. package/docs/init.md +101 -0
  132. package/docs/lessons.md +294 -0
  133. package/docs/log.md +139 -0
  134. package/docs/measure.md +178 -0
  135. package/docs/providers/claude-code.md +118 -0
  136. package/docs/providers/cursor.md +110 -0
  137. package/docs/providers/index.md +71 -0
  138. package/docs/troubleshooting.md +138 -0
  139. package/model-aliases.json +12 -0
  140. package/model-prices.cursor.json +410 -0
  141. package/model-prices.json +1 -0
  142. package/package.json +57 -0
  143. package/skills/harness-init/SKILL.md +241 -0
  144. package/skills/harness-init/evals/trigger_evals.json +86 -0
  145. package/skills/harness-init/references/capabilities.md +113 -0
  146. package/src/contracts/capabilities.ts +30 -0
  147. package/src/contracts/decision.ts +21 -0
  148. package/src/contracts/effort.ts +15 -0
  149. package/src/contracts/harness-event.ts +87 -0
  150. package/src/contracts/index.ts +6 -0
  151. package/src/contracts/policy-defaults.ts +14 -0
  152. package/src/contracts/wiring.ts +20 -0
  153. package/src/core/attest/attest.service.ts +0 -0
  154. package/src/core/capability/capability.service.ts +62 -0
  155. package/src/core/capability/capability.store.ts +51 -0
  156. package/src/core/capability/capability.types.ts +56 -0
  157. package/src/core/comment-policy/comment-policy.service.ts +307 -0
  158. package/src/core/comment-policy/comment-policy.types.ts +6 -0
  159. package/src/core/comment-policy/comment-resolvability.ts +134 -0
  160. package/src/core/comment-policy/comment-syntax.catalog.ts +310 -0
  161. package/src/core/comment-policy/comment-syntax.store.ts +65 -0
  162. package/src/core/comment-policy/comment-syntax.types.ts +24 -0
  163. package/src/core/core.facade.ts +461 -0
  164. package/src/core/duplication/duplication.service.ts +219 -0
  165. package/src/core/floor/floor.catalog.ts +54 -0
  166. package/src/core/floor/floor.paths.ts +90 -0
  167. package/src/core/floor/floor.policy-surface.ts +311 -0
  168. package/src/core/floor/floor.service.ts +306 -0
  169. package/src/core/floor/floor.tokenize.ts +179 -0
  170. package/src/core/floor/floor.verb.ts +28 -0
  171. package/src/core/floor/index.ts +1 -0
  172. package/src/core/gate/gate.artifact.ts +157 -0
  173. package/src/core/gate/gate.command.ts +130 -0
  174. package/src/core/gate/gate.findings.ts +201 -0
  175. package/src/core/gate/gate.inputs.ts +0 -0
  176. package/src/core/gate/gate.lock.ts +283 -0
  177. package/src/core/gate/gate.service.ts +28 -0
  178. package/src/core/gate/gate.types.ts +67 -0
  179. package/src/core/handoff/handoff.service.ts +43 -0
  180. package/src/core/handoff/handoff.store.ts +59 -0
  181. package/src/core/handoff/handoff.types.ts +89 -0
  182. package/src/core/index.ts +52 -0
  183. package/src/core/integrity/state-seal.ts +81 -0
  184. package/src/core/lesson/lesson.authored.ts +96 -0
  185. package/src/core/lesson/lesson.credit.ts +51 -0
  186. package/src/core/lesson/lesson.garden.ts +283 -0
  187. package/src/core/lesson/lesson.link.ts +93 -0
  188. package/src/core/lesson/lesson.score.ts +61 -0
  189. package/src/core/lesson/lesson.select.ts +209 -0
  190. package/src/core/lesson/lesson.service.ts +110 -0
  191. package/src/core/lesson/lesson.store.ts +317 -0
  192. package/src/core/lesson/lesson.sync.ts +70 -0
  193. package/src/core/lesson/lesson.types.ts +92 -0
  194. package/src/core/lesson/lesson.validity.ts +44 -0
  195. package/src/core/observability/observability.report.ts +326 -0
  196. package/src/core/observability/observability.service.ts +328 -0
  197. package/src/core/observability/observability.store.ts +269 -0
  198. package/src/core/observability/observability.types.ts +193 -0
  199. package/src/core/observability/observability.why.ts +206 -0
  200. package/src/core/observe/observe.service.ts +76 -0
  201. package/src/core/plan/plan.detect.ts +47 -0
  202. package/src/core/plan/plan.service.ts +81 -0
  203. package/src/core/plan/plan.types.ts +25 -0
  204. package/src/core/policy/policy.defaults.ts +103 -0
  205. package/src/core/policy/policy.guard.ts +34 -0
  206. package/src/core/policy/policy.integrity.ts +238 -0
  207. package/src/core/policy/policy.loader.ts +118 -0
  208. package/src/core/policy/policy.operator.ts +82 -0
  209. package/src/core/policy/policy.posture.ts +79 -0
  210. package/src/core/policy/policy.rails.ts +45 -0
  211. package/src/core/policy/policy.types.ts +157 -0
  212. package/src/core/presence/presence.service.ts +111 -0
  213. package/src/core/presence/presence.store.ts +59 -0
  214. package/src/core/presence/presence.types.ts +9 -0
  215. package/src/core/release/release.decisions.ts +147 -0
  216. package/src/core/release/release.seen.ts +38 -0
  217. package/src/core/shell-policy/shell-policy.service.ts +215 -0
  218. package/src/core/shell-policy/shell-policy.stall.ts +57 -0
  219. package/src/core/shell-policy/shell-policy.types.ts +26 -0
  220. package/src/core/ship/ship.ledger.ts +76 -0
  221. package/src/core/ship/ship.service.ts +137 -0
  222. package/src/core/ship/ship.types.ts +19 -0
  223. package/src/core/stagnation/stagnation.resolution.ts +87 -0
  224. package/src/core/stagnation/stagnation.service.ts +20 -0
  225. package/src/core/stagnation/stagnation.store.ts +49 -0
  226. package/src/core/stagnation/stagnation.types.ts +12 -0
  227. package/src/core/subagent-policy/subagent-policy.parent-model.ts +202 -0
  228. package/src/core/subagent-policy/subagent-policy.service.ts +139 -0
  229. package/src/core/subagent-policy/subagent-policy.types.ts +22 -0
  230. package/src/core/supply-chain/supply-chain.catalog.ts +51 -0
  231. package/src/core/supply-chain/supply-chain.service.ts +217 -0
  232. package/src/core/turn/turn.activity.ts +94 -0
  233. package/src/core/turn/turn.autopilot.ts +131 -0
  234. package/src/core/turn/turn.failure-signals.ts +193 -0
  235. package/src/core/turn/turn.loop-counter.ts +71 -0
  236. package/src/core/turn/turn.types.ts +14 -0
  237. package/src/core/untrusted/untrusted.detect.ts +53 -0
  238. package/src/core/untrusted/untrusted.recall.ts +87 -0
  239. package/src/core/untrusted/untrusted.service.ts +129 -0
  240. package/src/core/untrusted/untrusted.store.ts +75 -0
  241. package/src/core/untrusted/untrusted.types.ts +42 -0
  242. package/src/entrypoints/compact-before.ts +14 -0
  243. package/src/entrypoints/prompt-submit.ts +25 -0
  244. package/src/entrypoints/response-after.ts +60 -0
  245. package/src/entrypoints/run.ts +179 -0
  246. package/src/entrypoints/session-end.ts +58 -0
  247. package/src/entrypoints/session-start.ts +172 -0
  248. package/src/entrypoints/shim.ts +46 -0
  249. package/src/entrypoints/stop.ts +876 -0
  250. package/src/entrypoints/subagent-start.ts +16 -0
  251. package/src/entrypoints/subagent-stop.ts +32 -0
  252. package/src/entrypoints/support.ts +182 -0
  253. package/src/entrypoints/tool-after.ts +111 -0
  254. package/src/entrypoints/tool-before.ts +186 -0
  255. package/src/entrypoints/tool-failure.ts +26 -0
  256. package/src/platform/backoff.ts +49 -0
  257. package/src/platform/cli-output.ts +30 -0
  258. package/src/platform/env-scope.ts +22 -0
  259. package/src/platform/fs-atomic.ts +142 -0
  260. package/src/platform/fs-jsonl.ts +24 -0
  261. package/src/platform/git.ts +187 -0
  262. package/src/platform/paths.ts +80 -0
  263. package/src/platform/pricing.ts +229 -0
  264. package/src/platform/process.ts +64 -0
  265. package/src/platform/sanitize.ts +23 -0
  266. package/src/platform/screen.ts +46 -0
  267. package/src/platform/style.ts +103 -0
  268. package/src/providers/claude/claude.capabilities.ts +25 -0
  269. package/src/providers/claude/claude.detect.ts +14 -0
  270. package/src/providers/claude/claude.inbound.ts +252 -0
  271. package/src/providers/claude/claude.lessons-view.ts +33 -0
  272. package/src/providers/claude/claude.outbound.ts +65 -0
  273. package/src/providers/claude/claude.policy-defaults.ts +9 -0
  274. package/src/providers/claude/claude.transcript.ts +62 -0
  275. package/src/providers/claude/claude.wiring.ts +245 -0
  276. package/src/providers/claude/index.ts +17 -0
  277. package/src/providers/cursor/cursor.capabilities.ts +28 -0
  278. package/src/providers/cursor/cursor.detect.ts +14 -0
  279. package/src/providers/cursor/cursor.inbound.ts +203 -0
  280. package/src/providers/cursor/cursor.lessons-view.ts +30 -0
  281. package/src/providers/cursor/cursor.outbound.ts +43 -0
  282. package/src/providers/cursor/cursor.policy-defaults.ts +9 -0
  283. package/src/providers/cursor/cursor.wiring.ts +214 -0
  284. package/src/providers/cursor/index.ts +17 -0
  285. package/src/providers/index.ts +9 -0
  286. package/src/providers/provider.degrade.ts +152 -0
  287. package/src/providers/provider.port.ts +20 -0
  288. package/src/providers/provider.registry.ts +28 -0
  289. package/tools/doctor.ts +631 -0
  290. package/tools/help-topic.ts +74 -0
  291. package/tools/init-project.ts +277 -0
  292. package/tools/install-runtime.ts +164 -0
  293. package/tools/lessons-cli.ts +474 -0
  294. package/tools/obs-cli.ts +175 -0
  295. package/tools/price-lookup.ts +57 -0
  296. package/tools/refresh-model-prices.ts +210 -0
  297. package/tools/test-env.mjs +28 -0
  298. package/tools/test-env.names.d.mts +1 -0
  299. package/tools/test-env.names.mjs +14 -0
  300. package/tools/uninstall-runtime.ts +463 -0
@@ -0,0 +1,118 @@
1
+ ---
2
+ type: Provider
3
+ title: "Claude Code provider"
4
+ description: "The Claude Code adapter — capability descriptor, event mapping, and wiring target for the settings.json hooks block in Claude Code's resolved config directory."
5
+ tags: [provider, claude-code]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # Claude Code provider
10
+
11
+ Source: `src/providers/claude/`.
12
+
13
+ ## Detection
14
+
15
+ `claude.detect.ts`: a raw hook payload is Claude Code's when `hook_event_name` is PascalCase (e.g.
16
+ `PreToolUse`) and either `cwd` or `transcript_path` is present.
17
+
18
+ ## Capability descriptor
19
+
20
+ `claude.capabilities.ts`:
21
+
22
+ | Capability | Value |
23
+ | --- | --- |
24
+ | `enforcesHooks` | `true` |
25
+ | `askSupportedOn` | `["tool.before", "shell.before", "mcp.before", "read.before"]` — includes `tool.before`, unlike Cursor (see [/decisions/ad-009.md](/decisions/ad-009.md), note) |
26
+ | `sessionEnv` | `false` |
27
+ | `nativeLoopCounter` | `false` — `Stop` carries no loop counter; `core/turn` supplies it instead (see [/decisions/ad-014.md](/decisions/ad-014.md)) |
28
+ | `dedicatedShellEvent` | `false` — shell is `PreToolUse`/`PostToolUse` with `tool_name: "Bash"` |
29
+ | `toolInputRewrite` | `true` |
30
+ | `toolOutputRewrite` | `true` |
31
+ | `contextAtToolBefore` | `true` |
32
+ | `contextAtToolAfter` | `true` |
33
+ | `contextAtStop` | `true` — `Stop` accepts `hookSpecificOutput.additionalContext` for feedback that continues the turn |
34
+ | `sessionStartContextReliable` | `true` — `SessionStart` delivers `hookSpecificOutput.additionalContext`, capped at 10,000 characters |
35
+ | `usageInPayload` | `false` — cost comes from the transcript, not the hook payload |
36
+ | `effortSignal` | `true` — `effort.level` (`low\|medium\|high\|xhigh\|max`) |
37
+ | `thoughtEvent` | `false` |
38
+
39
+ ## Policy defaults
40
+
41
+ `claude.policy-defaults.ts` supplies no model allowlist and no blocked patterns — see
42
+ [/decisions/ad-011.md](/decisions/ad-011.md).
43
+
44
+ It used to ship three model slugs, and an empty project list fell back to them, so a spawn could be refused by a
45
+ list that appears nowhere in the project. This page named a fourth that the code did not, which is what a shipped
46
+ catalogue does over time. `subagents.allowedModels` is the operator's and has no other source; an empty one
47
+ enforces nothing and `doctor` says so ([/decisions/ad-053.md](/decisions/ad-053.md)).
48
+
49
+ ## Event mapping
50
+
51
+ `claude.inbound.ts` maps Claude's PascalCase hook names to `HarnessEventKind`. Unlike Cursor, `PreToolUse`
52
+ and `PostToolUse` are single dispatcher hooks that fan out by `tool_name`:
53
+
54
+ | Claude hook | Fan-out rule | `HarnessEventKind` |
55
+ | --- | --- | --- |
56
+ | `SessionStart` | — | `session.start` |
57
+ | `SessionEnd` | — | `session.end` |
58
+ | `UserPromptSubmit` | — | `prompt.submit` |
59
+ | `PreToolUse` | `tool_name === "Bash"` | `shell.before` |
60
+ | `PreToolUse` | `tool_name` matches `mcp__*` | `mcp.before` |
61
+ | `PreToolUse` | `tool_name === "Read"` | `read.before` |
62
+ | `PreToolUse` | otherwise | `tool.before` |
63
+ | `PostToolUse` | `tool_name === "Bash"` | `shell.after` |
64
+ | `PostToolUse` | `tool_name` matches `mcp__*` | `mcp.after` |
65
+ | `PostToolUse` | `tool_name` is `Edit`/`Write` | `edit.after` |
66
+ | `PostToolUse` | otherwise | `tool.after` |
67
+ | `PostToolUseFailure` | — | `tool.failure` |
68
+ | `SubagentStart` | — | `subagent.start` |
69
+ | `SubagentStop` | — | `subagent.stop` |
70
+ | `Stop` | — | `stop` |
71
+ | `PreCompact` | — | `compact.before` |
72
+ | `MessageDisplay` | — | `response.after` |
73
+
74
+ Claude has no `thought.after` equivalent (`thoughtEvent: false`).
75
+
76
+ ## Field paths
77
+
78
+ Exact JSON field paths this adapter reads are pinned in
79
+ [/decisions/ad-014.md](/decisions/ad-014.md) — including the one inferred rather than documented
80
+ (`tool_input.file_path` for Edit/Write/Read).
81
+
82
+ ## Wiring target
83
+
84
+ `claude.wiring.ts` merges (`strategy: "merge"`) into `~/.claude/settings.json`'s `hooks` block —
85
+ never replacing the file wholesale, since a user's own Claude settings may already exist. Every entry uses
86
+ exec form (`command: "node"`, `args: [launcherPath, handler]`) on every platform, bypassing shell
87
+ tokenization so there is no quoting variant to get wrong. Handler names are the
88
+ `src/entrypoints/<name>.ts` filenames (see [/decisions/ad-015.md](/decisions/ad-015.md)):
89
+ `session-start`, `session-end`, `prompt-submit`, `tool-before`, `tool-after`, `tool-failure`,
90
+ `subagent-start`, `subagent-stop`, `stop`, `compact-before`, `response-after`.
91
+
92
+ The merge is idempotent and deep-equality-checked per hook group, so re-running it never duplicates an
93
+ already-present entry.
94
+
95
+ ## Lessons view
96
+
97
+ `claude.lessons-view.ts` appends a single `@.tlc/harness/lessons.md` import line to the project's `CLAUDE.md` when it
98
+ is not already present. It is a pointer, not a copy — the content lives in one file.
99
+
100
+ It is written under `intelligence.lessons.syncRulesFile: "always"` and **not** under the default `"auto"`, because
101
+ this adapter declares `sessionStartContextReliable: true`: `SessionStart` delivers
102
+ `hookSpecificOutput.additionalContext`, so lessons arrive without a durable file and the pointer would be a second
103
+ copy of a working route. Set `always` to have it anyway — it survives a restart, which the injected context does not
104
+ (see [/decisions/ad-050.md](/decisions/ad-050.md), and [/decisions/ad-011.md](/decisions/ad-011.md) item 4).
105
+
106
+ Only `hookSpecificOutput.additionalContext` is emitted. Claude Code reads a top-level `additional_context` as well
107
+ and does not deduplicate the two, so emitting both would inject the same text twice.
108
+
109
+ ## Doctor / status
110
+
111
+ `tlc harness doctor` reports Claude wiring as `wired` when merging the current entries into the existing
112
+ `~/.claude/settings.json` would produce no change, `detected-but-unwired` otherwise, and `not-installed`
113
+ when `~/.claude` does not exist.
114
+
115
+ ## See also
116
+
117
+ - [/providers/index.md](/providers/index.md)
118
+ - [/providers/cursor.md](/providers/cursor.md)
@@ -0,0 +1,110 @@
1
+ ---
2
+ type: Provider
3
+ title: "Cursor provider"
4
+ description: "The Cursor adapter — capability descriptor, event mapping, and wiring target for Cursor's hooks.json."
5
+ tags: [provider, cursor]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # Cursor provider
10
+
11
+ Source: `src/providers/cursor/`.
12
+
13
+ ## Detection
14
+
15
+ `cursor.detect.ts`: a raw hook payload is Cursor's when `hook_event_name` is camelCase (e.g.
16
+ `beforeShellExecution`) and `workspace_roots` is an array.
17
+
18
+ ## Capability descriptor
19
+
20
+ `cursor.capabilities.ts`:
21
+
22
+ | Capability | Value |
23
+ | --- | --- |
24
+ | `enforcesHooks` | `true` |
25
+ | `askSupportedOn` | `["shell.before", "mcp.before"]` — **not** `tool.before` (see [/decisions/ad-009.md](/decisions/ad-009.md), note) |
26
+ | `sessionEnv` | `true` |
27
+ | `nativeLoopCounter` | `true` |
28
+ | `dedicatedShellEvent` | `true` |
29
+ | `toolInputRewrite` | `true` |
30
+ | `toolOutputRewrite` | `true` |
31
+ | `contextAtToolBefore` | `false` |
32
+ | `contextAtToolAfter` | `true` |
33
+ | `contextAtStop` | `false` — the `stop` output schema carries `followup_message` and nothing else |
34
+ | `sessionStartContextReliable` | `false` — Cursor accepts `additional_context` at `sessionStart`, logs it as merged, and drops it (see [Lessons view](#lessons-view)) |
35
+ | `usageInPayload` | `true` |
36
+ | `effortSignal` | `false` |
37
+ | `thoughtEvent` | `true` |
38
+
39
+ ## Policy defaults
40
+
41
+ `cursor.policy-defaults.ts` supplies the blocked-pattern list (`-fast(?:$|[^a-z0-9])`, `/fast(?:$|[^a-z0-9])`,
42
+ `composer-2\.5-fast`) — see [/decisions/ad-011.md](/decisions/ad-011.md).
43
+
44
+ **It supplies no model allowlist.** It used to, and an empty project list fell back to it, so a spawn could be
45
+ refused by five slugs that appear nowhere in the project and had already gone stale. `subagents.allowedModels` is
46
+ the operator's and has no other source; an empty one enforces nothing and `doctor` says so
47
+ ([/decisions/ad-053.md](/decisions/ad-053.md)). The blocked patterns stay because they are **added** to the
48
+ project's rather than replacing them.
49
+
50
+ ## Event mapping
51
+
52
+ `cursor.inbound.ts` maps Cursor's own camelCase hook names to the shared `HarnessEventKind`:
53
+
54
+ | Cursor hook | `HarnessEventKind` |
55
+ | --- | --- |
56
+ | `sessionStart` | `session.start` |
57
+ | `sessionEnd` | `session.end` |
58
+ | `beforeSubmitPrompt` | `prompt.submit` |
59
+ | `preToolUse` | `tool.before` |
60
+ | `postToolUse` | `tool.after` |
61
+ | `postToolUseFailure` | `tool.failure` |
62
+ | `beforeShellExecution` | `shell.before` |
63
+ | `afterShellExecution` | `shell.after` |
64
+ | `beforeMCPExecution` | `mcp.before` |
65
+ | `afterMCPExecution` | `mcp.after` |
66
+ | `beforeReadFile` | `read.before` |
67
+ | `afterFileEdit` | `edit.after` |
68
+ | `subagentStart` | `subagent.start` |
69
+ | `subagentStop` | `subagent.stop` |
70
+ | `stop` | `stop` |
71
+ | `preCompact` | `compact.before` |
72
+ | `afterAgentResponse` | `response.after` |
73
+ | `afterAgentThought` | `thought.after` |
74
+
75
+ Cursor has a dedicated event per tool class (`beforeShellExecution`, `beforeMCPExecution`,
76
+ `beforeReadFile`), unlike Claude's single `PreToolUse`/`PostToolUse` fan-out.
77
+
78
+ ## Wiring target
79
+
80
+ `cursor.wiring.ts` writes (`strategy: "replace"`) the user-level `~/.cursor/hooks.json`, one entry per
81
+ `(hookEvent, handler)` pair, dispatching through the launcher: `node <launcherPath> <handler>` on
82
+ Unix/macOS, `cmd /c node <launcherPath> <handler>` on Windows. Handler names are the
83
+ `src/entrypoints/<name>.ts` filenames (see [/decisions/ad-015.md](/decisions/ad-015.md)):
84
+ `session-bootstrap`, `persist-handoff`, `obs-session-end`, `obs-passive`, `guard-subagent`,
85
+ `pre-tool-use`, `guard-shell`, `audit-event`, `guard-mcp`, `guard-read`, `format`, `verify-gates`,
86
+ `obs-stop`, `track-response`.
87
+
88
+ ## Lessons view
89
+
90
+ `cursor.lessons-view.ts` renders `.tlc/harness/lessons.md` into `.cursor/rules/harness-lessons.mdc`
91
+ (`alwaysApply: true`). This is not a second copy of a working route — it is **the** route on this host. Cursor
92
+ accepts `additional_context` returned from `sessionStart`, logs it as merged, and drops it; its own staff called that
93
+ "a bug on our side… a timing issue between when the hook runs and when the composer handle is created" (forum thread
94
+ 158452, 2026-04-20), and it was reported again against 3.14.7 on 2026-08-02. `env` on the same payload arrives,
95
+ because that is a different code path — which is why `HARNESS_ACTIVE` works while the prose does not.
96
+
97
+ The adapter therefore declares `sessionStartContextReliable: false`, and the default `syncRulesFile: "auto"` writes
98
+ the view here for that reason rather than because an operator guessed. `never` declines it; `always` forces it (see
99
+ [/decisions/ad-050.md](/decisions/ad-050.md), and [/decisions/ad-011.md](/decisions/ad-011.md) item 4 for the
100
+ original reasoning).
101
+
102
+ ## Doctor / status
103
+
104
+ `tlc harness doctor` reports Cursor wiring as `wired`, `detected-but-unwired`, or `not-installed` by
105
+ diffing the live `~/.cursor/hooks.json` against the entries this adapter would write.
106
+
107
+ ## See also
108
+
109
+ - [/providers/index.md](/providers/index.md)
110
+ - [/providers/claude-code.md](/providers/claude-code.md)
@@ -0,0 +1,71 @@
1
+ ---
2
+ type: Aggregate
3
+ title: "Providers index"
4
+ description: "Index of the provider adapters — Cursor and Claude Code — and the port they both implement."
5
+ tags: [providers, index, architecture]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # Providers
10
+
11
+ Core steering logic never imports a provider adapter and never reads a provider name. Each provider is an
12
+ anti-corruption-layer adapter implementing `ProviderPort`
13
+ (`src/providers/provider.port.ts`):
14
+
15
+ ```ts
16
+ type ProviderPort = {
17
+ readonly name: string;
18
+ detect(raw: unknown): boolean;
19
+ capabilities(): ProviderCapabilities;
20
+ policyDefaults(): ProviderPolicyDefaults;
21
+ toEvent(raw: Record<string, unknown>): HarnessEvent | null;
22
+ render(decision: Decision, event: HarnessEvent): Rendered;
23
+ wiring(runtime: RuntimePaths): ProviderWiring;
24
+ };
25
+ ```
26
+
27
+ - `detect` — does this raw hook payload belong to this provider?
28
+ - `capabilities` — a declarative `ProviderCapabilities` descriptor (see
29
+ [/architecture.md](/architecture.md)); core degrades on this data, never on `name`.
30
+ - `policyDefaults` — this provider's own model allowlist / blocked patterns / minimum effort (see
31
+ [/decisions/ad-011.md](/decisions/ad-011.md)).
32
+ - `toEvent` — parses a raw hook payload into the shared `HarnessEvent` shape.
33
+ - `render` — turns a core `Decision` back into this provider's wire format.
34
+ - `wiring` — describes which hooks this provider needs registered, and where.
35
+
36
+ ## Registered providers
37
+
38
+ Detection runs in registry order (`src/providers/provider.registry.ts`), first match wins, and multiple
39
+ matches are reported as ambiguous rather than silently resolved:
40
+
41
+ | Provider | Detected by | Docs |
42
+ | --- | --- | --- |
43
+ | Cursor | camelCase `hook_event_name` + `workspace_roots` array | [/providers/cursor.md](/providers/cursor.md) |
44
+ | Claude Code | PascalCase `hook_event_name` + `cwd` or `transcript_path` | [/providers/claude-code.md](/providers/claude-code.md) |
45
+
46
+ ## Event kinds
47
+
48
+ Both adapters translate into the same 18-member `HarnessEventKind` union (see
49
+ [/decisions/ad-009.md](/decisions/ad-009.md) item 1): `session.start`, `session.end`, `prompt.submit`,
50
+ `tool.before`, `tool.after`, `tool.failure`, `shell.before`, `shell.after`, `mcp.before`, `mcp.after`,
51
+ `read.before`, `edit.after`, `subagent.start`, `subagent.stop`, `stop`, `compact.before`, `response.after`,
52
+ `thought.after`. A provider that cannot produce a kind is gated by its capability descriptor, not by the
53
+ kind's absence.
54
+
55
+ ## Caller identity vs. spawn target
56
+
57
+ `HarnessEvent` splits two fields that an earlier design conflated (see
58
+ [/decisions/ad-016.md](/decisions/ad-016.md) item 1):
59
+
60
+ | Field | Meaning |
61
+ | --- | --- |
62
+ | `subagentType` | the identity of the agent **currently running** (the caller) |
63
+ | `spawnSubagentType` | the type of subagent a Task/spawn call **targets** |
64
+ | `model` | the current/parent model |
65
+ | `spawnModel` | the model of the child being spawned |
66
+
67
+ ## Adding a provider
68
+
69
+ A new provider is a new directory under `src/providers/<name>/` plus one line in
70
+ `src/providers/provider.registry.ts` (see [/decisions/ad-004.md](/decisions/ad-004.md) and
71
+ [/decisions/ad-009.md](/decisions/ad-009.md) item 7) — never a change to `core/`.
@@ -0,0 +1,138 @@
1
+ ---
2
+ type: Runbook
3
+ title: "Was that the harness?"
4
+ description: "How to tell a harness decision from model behaviour, in the moment and after the fact: the one command that answers it, what each rule name means, and the four symptoms that are not the harness at all."
5
+ tags: [troubleshooting, rules, observability]
6
+ timestamp: "2026-08-12"
7
+ ---
8
+
9
+ # Was that the harness?
10
+
11
+ > "eu to meio perdido tentando entender o que é comportamento do harness toolkit, o que é loucura do modelo"
12
+
13
+ Hooks are invisible by construction: the harness answers the editor, and the editor decides whether to show
14
+ you. This page is how you find out anyway.
15
+
16
+ ## The one command
17
+
18
+ ```bash
19
+ tlc harness why
20
+ ```
21
+
22
+ The last ten decisions the tool made, newest first, each with the rule behind it:
23
+
24
+ ```
25
+ Last 4 harness decision(s), newest first:
26
+
27
+ 19:23:05 shell deny rule=policy-surface-write
28
+ python3 -c "open('.tlc/harness/config.json','w')"
29
+ 19:23:05 shell ask rule=shell-catastrophic
30
+ dd if=/dev/zero of=/dev/sda
31
+ 19:23:05 tool.before deny rule=subagent-allowlist
32
+ Task
33
+ 19:23:05 session start context rule=unattributed
34
+ 4210 chars injected
35
+ ```
36
+
37
+ **And when it was not the harness, it says so:**
38
+
39
+ ```
40
+ No harness decision in this window.
41
+ Whatever you just saw was the model, not a rail — the harness allowed everything it was asked about.
42
+ ```
43
+
44
+ That sentence is the point of the command. `tlc harness why 30` widens the window; `--json` gives the same
45
+ records as data.
46
+
47
+ ## The rule names
48
+
49
+ Every refusal names one. The name is the pointer — the reasoning lives in the decision record it cites.
50
+
51
+ | Rule | What refused, and why |
52
+ | --- | --- |
53
+ | `outside-project-destruction` | a destructive command aimed outside the repo and outside the temp directory |
54
+ | `unprovable-destruction` | a destructive verb whose target is built at runtime, so the harness cannot see what it would delete |
55
+ | `unprovable-execution` | a program fetched over the network and handed to a shell, so the harness cannot read what would run |
56
+ | `secret-access` | a read that would copy credentials into the transcript, from a file **or** from the instance metadata service |
57
+ | `history-rewrite` | `git push --force`. `--force-with-lease` is allowed |
58
+ | `machine-control` | `shutdown`, `reboot`, `halt`, `poweroff` |
59
+ | `policy-surface-write` | any route an agent has to harness policy or state |
60
+ | `policy-baseline-divergence` | a policy file changed mid-session with no `tlc harness` command behind it |
61
+ | `untrusted-command` | a shell command that appears verbatim in content this session fetched — put to you before it runs |
62
+ | `edit-collision` | another live session touched this file recently |
63
+ | `shell-catastrophic` | a shell command that can destroy data outside the workspace |
64
+ | `shell-posture-paired` | the `paired` posture asks before a command that leaves the machine or can overwrite a path |
65
+ | `shell-stall` | the same shell command repeated past the threshold |
66
+ | `subagent-allowlist` | the model is not on `subagents.allowedModels` |
67
+ | `subagent-parent-fast` | the parent chat is in Fast mode |
68
+ | `subagent-blocked-pattern` | the model matched a blocked shape, `*-fast` by default |
69
+ | `subagent-model-required` | the spawn named no model and `requireModel` is on |
70
+ | `subagent-min-effort` | the spawn's effort is below `minEffort` |
71
+ | `subagent-read-only` | a read-only subagent type reached for a writing tool |
72
+ | `rewrite-unavailable` | the provider cannot rewrite tool input, so the harness asked instead |
73
+ | `unattributed` | a record written before rules were required. It will not appear for new decisions |
74
+
75
+ ## Four things that are **not** the harness
76
+
77
+ **A message with no `rule=`, no `BLOCKED:` and no `FLOOR:`.** Every harness decision carries one of the three.
78
+
79
+ **A subagent model changing.** The harness does not change models. It refuses a spawn, with
80
+ `rule=subagent-allowlist`, and the refusal goes to the model as `agent_message` and to you as `user_message`.
81
+ If your editor renders neither, `tlc harness why` still shows it.
82
+
83
+ **A gate failing here but passing in your terminal.** From the second attempt the follow-up says so itself, and
84
+ names the variables the hook set. It is usually the project's gate command differing from the one your suite
85
+ needs — which only you can change:
86
+
87
+ ```bash
88
+ tlc harness gate test-command <your real test command>
89
+ ```
90
+
91
+ **Everything, when the harness is off for this repo.** `tlc harness doctor` lists which rails are on.
92
+
93
+ ## After the fact
94
+
95
+ | Command | Answers |
96
+ | --- | --- |
97
+ | `tlc harness why [n]` | the last n decisions, with rules — **start here** |
98
+ | `tlc harness obs report` | this session: gate outcomes, refusals by rule, interruptions by rule, cost |
99
+ | `tlc harness obs live` | the same signal as it happens |
100
+ | `tlc harness doctor` | which rails are on, and which are on but enforcing nothing |
101
+ | `tlc harness handoff` | what the last turn left open |
102
+ | `tlc harness attest` | one hash-chained record per session, for a reviewer |
103
+
104
+ ## When a refusal is wrong
105
+
106
+ Say so in your reply and let the operator decide. Working around a floor rule is not a fix — the six of them
107
+ read no configuration precisely so that nothing in a session can clear them
108
+ ([/decisions/ad-016.md](/decisions/ad-016.md)).
109
+
110
+ For a rail, the operator changes it from their own terminal, outside the agent session
111
+ ([/decisions/ad-022.md](/decisions/ad-022.md)):
112
+
113
+ ```bash
114
+ tlc harness mode solo # leave the paired posture
115
+ tlc harness pause # disable stop checks while exploring
116
+ tlc harness policy accept --all # after you edited config mid-session
117
+ ```
118
+
119
+ ## When you want it off entirely
120
+
121
+ ```bash
122
+ tlc harness uninstall # the plan, and nothing else
123
+ tlc harness uninstall --yes # apply it
124
+ ```
125
+
126
+ It un-merges the hook groups out of `settings.json` and leaves every other key alone, so this is the
127
+ supported alternative to editing that file by hand. `config.json` and `state/` survive unless you add
128
+ `--purge`.
129
+
130
+ **An agent cannot do this for you**, and the refusal is not a defect. The runtime home sits outside any
131
+ project and `state/` is a policy surface, so a delegated uninstall meets `outside-project-destruction` or
132
+ `policy-surface-write` ([/decisions/ad-066.md](/decisions/ad-066.md)). Run it from your own terminal.
133
+
134
+ ## See also
135
+
136
+ - [/concepts.md](/concepts.md) — every rail from the operator's side
137
+ - [/diagnose.md](/diagnose.md) — hooks not firing, stale runtime, cost showing null
138
+ - [/decisions/index.md](/decisions/index.md) — why each rule exists
@@ -0,0 +1,12 @@
1
+ {
2
+ "composer-2.5": "composer-2.5",
3
+ "cursor-grok-4.5-high": "grok-4.5",
4
+ "cursor-grok-4.5": "grok-4.5",
5
+ "glm-5.2-high": "glm-5.2",
6
+ "glm-5.2": "glm-5.2",
7
+ "kimi-k2.7-code": "kimi-k2.7-code",
8
+ "gpt-5.3-codex-high": "gpt-5.3-codex",
9
+ "gpt-5.3-codex": "gpt-5.3-codex",
10
+ "auto": "auto-cost",
11
+ "auto-cost": "auto-cost"
12
+ }