@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,248 @@
1
+ ---
2
+ type: Runbook
3
+ title: "Diagnose"
4
+ description: "Checklist for hooks not firing, Node vs Bun runtime confusion, stale runtime, subagent denials, cost showing null, and double hooks — for both Cursor and Claude Code."
5
+ tags: [runbook, diagnose, troubleshooting]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # Diagnose
10
+
11
+ Run `tlc harness doctor` first. Then walk this checklist.
12
+
13
+ ## Hooks not firing
14
+
15
+ **Cursor**
16
+
17
+ 1. Confirm the Cursor config directory's `hooks.json` invokes `node …/tlc-exec.mjs <handler>` (installers
18
+ write this). `tlc harness doctor` prints the resolved path; `CURSOR_CONFIG_DIR` overrides the default.
19
+ 2. `dist/*.mjs` must exist (`tlc harness build`).
20
+ 3. Reload Cursor after editing hooks.
21
+ 4. Open **View → Output → Hooks** for exit errors.
22
+ 5. Project shim should call `tlc-exec shim <handler>`; with the global `sessionStart` hook set,
23
+ `TLC_ACTIVE=1` makes the shim no-op (expected).
24
+
25
+ **Claude Code**
26
+
27
+ 1. Confirm the Claude config directory's `settings.json` (resolved, `CLAUDE_CONFIG_DIR` overrides the
28
+ default) has a `hooks` block with entries whose `command` is `node` and whose
29
+ `args` start with the launcher path (see [/providers/claude-code.md](/providers/claude-code.md)).
30
+ 2. `dist/*.mjs` must exist (`tlc harness build`).
31
+ 3. Restart the Claude Code session after editing `settings.json`.
32
+ 4. Project shim should call `tlc-exec shim <handler>`.
33
+
34
+ On Windows, Cursor hooks use `cmd /c node "…\tlc-exec.mjs" …`; Claude Code hooks stay exec-form
35
+ (`node …`) on every platform.
36
+
37
+ ## Node vs Bun
38
+
39
+ - Preferred: **Bun** on PATH — every hook runs the TypeScript source directly, ~1 ms per invocation.
40
+ - Guaranteed fallback: **Node 24+** + `tlc harness build` (`dist/*.mjs`), ~27 ms per invocation.
41
+ - `tlc harness doctor` reports the resolved runtime as `OK` (Bun found) or `WARN` (Node fallback, with the
42
+ measured cost of the gap and the one-line fix). See [/decisions/ad-012.md](/decisions/ad-012.md).
43
+ - Missing dist with Node present: run `tlc harness build` (needs Bun or esbuild once to compile).
44
+
45
+ ## `update` aborts on `dist/` and keeps aborting
46
+
47
+ The one manual step, once, on every platform:
48
+
49
+ ```bash
50
+ npm i -g @tech-leads-club/harness-toolkit@latest
51
+ tlc harness install
52
+ ```
53
+
54
+ **Why it has to be that and not `update`.** `update` runs from the installed runtime, and the fix for `update` is in
55
+ the revision `update` has to fetch — so a stuck install cannot deliver its own fix. The registry serves the package
56
+ independently of what is installed, which makes it the only route that does not depend on the thing that is stuck
57
+ ([/decisions/ad-048.md](/decisions/ad-048.md)).
58
+
59
+ Installing over a managed checkout moves it to `origin/main` with a hard reset. `config.json` and `state/` are gitignored,
60
+ so your policy, global lessons and obs history survive it. A **linked** runtime — a symlink to your own clone — is
61
+ left completely alone.
62
+
63
+ There is no `--force`: a managed runtime is already reset, and a linked clone is never written to.
64
+
65
+ ## Stale runtime / need latest main
66
+
67
+ ```bash
68
+ tlc harness update
69
+ ```
70
+
71
+ Then reload/restart the provider session.
72
+
73
+ **What update may write depends on what the runtime path is.** `tlc harness doctor` prints it as
74
+ `runtime ownership` ([/decisions/ad-046.md](/decisions/ad-046.md)):
75
+
76
+ | Kind | What update does |
77
+ | --- | --- |
78
+ | `managed checkout` | fetches and moves it to upstream with a hard reset — the harness owns its contents, so a local change there is never yours |
79
+ | `link to a working clone` | **nothing** in the clone. Refreshes only the CLI link, the skill link and provider hooks. Pull that clone yourself |
80
+ | `installed from npm` | bumps the package to `@latest` and re-materialises the runtime. No git command runs against it |
81
+ | `not a git checkout` | nothing to pull — install the package and run `tlc harness install` |
82
+
83
+ Update never rebuilds `dist/` when every bundle is present. It used to, and because Bun and esbuild emit different
84
+ bytes for the same source, the rebuild left the checkout permanently dirty and every later update failed. If you see
85
+ `update: dist/ complete — no rebuild`, that is the fix working.
86
+
87
+ After update, `tlc harness doctor` reports non-blocking `WARN:` lines for off/missing opt-ins (and for
88
+ default-on features you explicitly set to `false`). They do not fail doctor by themselves. A missing
89
+ `.tlc/harness/config.json` still fails the project-policy check until you init.
90
+
91
+ ## `tlc: command not found`
92
+
93
+ Re-run the platform installer, or ensure the CLI shim is on PATH:
94
+
95
+ - Unix: `~/.local/bin/tlc` → `~/.tlc/harness/bin/tlc`
96
+ - Windows: `%USERPROFILE%\.local\bin\tlc.cmd`
97
+
98
+ ## Obs empty / no signal
99
+
100
+ 1. `observability.enabled` must not be `false` in config.
101
+ 2. Happy-path tool/shell events are **debug** — enable `debugEnabled` or look for signal kinds only.
102
+ 3. Confirm `.tlc/harness/state/` is writable in the project.
103
+ 4. `tlc harness obs live` after a prompt submit / stop / denial.
104
+
105
+ ## The floor blocked a command that only reads harness state
106
+
107
+ Reading is ordinary work and the bootstrap asks for it, so a refusal there means the *verb* could not be proven to
108
+ only read — not that reading is forbidden. The refusal now says so and names the way through
109
+ ([/decisions/ad-047.md](/decisions/ad-047.md)):
110
+
111
+ ```bash
112
+ tlc harness handoff # handoff state, no shell needed
113
+ tlc harness policy # the resolved policy
114
+ tlc harness handoff --json # same, for a script
115
+ ```
116
+
117
+ Proven readers on the policy surface: `cat`, `head`, `tail`, `less`, `more`, `grep`, `rg`, `jq`, `ls`, `stat`,
118
+ `file`, `wc`, `cmp`, `diff`, `od`, `xxd`, `strings`, `md5sum`, `sha256sum`, `echo`, `printf`, `test`, `[`, and
119
+ `git show|diff|log|status|ls-files|cat-file|blame`.
120
+
121
+ `awk` and `sort` are **not** readers, on purpose — `awk '{print > f}'` and `sort -o f` write a file the head verb
122
+ never reveals. A redirect onto the surface is denied whatever the verb, so `test -f x > config.json` still fails.
123
+
124
+ ## The gate runs on a turn that changed nothing
125
+
126
+ It does not any more, and this is how to confirm it. A verdict is keyed on a content hash of the gate command and
127
+ the files it ran against; a match reuses the verdict without executing the command
128
+ ([/decisions/ad-045.md](/decisions/ad-045.md)).
129
+
130
+ ```bash
131
+ tlc harness obs report # the Gate time table has a Reused column
132
+ ```
133
+
134
+ | Reading | Meaning |
135
+ | --- | --- |
136
+ | `Runs` climbing on every turn | the inputs really are changing, or the hash is incomplete |
137
+ | `Reused` climbing | the verdict stood and the command did not run |
138
+ | both zero | the gate never ran; check `grind.enabled` and `codePaths` |
139
+
140
+ The hash is **incomplete** — so the gate always runs — when an input cannot be read, which includes a tracked file
141
+ that was deleted, or when the changed set exceeds 400 files or 12 MB. A gate that depends on something outside the
142
+ changed files (a database, a service, an environment variable) can also reuse a verdict that no longer holds.
143
+
144
+ ## A lesson is not reaching the turn
145
+
146
+ `tlc harness lessons list` answers it directly — a lesson that is being withheld is marked `WITHHELD` and the
147
+ notes on its line say why.
148
+
149
+ | Note | Meaning | What to do |
150
+ | --- | --- | --- |
151
+ | `stale=path-missing` / `symbol-missing` | a `--ref` no longer resolves | restore or rename the ref, then `tlc harness lessons garden` |
152
+ | `validity=expired` | past its `--until` | the next garden prunes it; write a new one |
153
+ | `validity=pending` | its window has not opened | wait, or rewrite without `--from` |
154
+ | `validity=invalid` | an unparseable bound, so it fails closed | rewrite with an ISO date |
155
+ | `WITHHELD` with no note | a **global** lesson whose refs do not resolve in this repository | expected — it applies where it came from |
156
+
157
+ Nothing withheld and still absent? Then it lost on rank or budget, not on health:
158
+
159
+ 1. `enabled=true` on the last line of `lessons list`.
160
+ 2. `status` must be `active` for session injection; a `candidate` only shows on a matching retry.
161
+ 3. **The budget usually binds.** `maxCharsSession` defaults to 900 and fits about two blocks while
162
+ `maxInjectSession` says five. The injected block names what it dropped; raising `maxCharsSession` is the fix.
163
+ 4. **A rule you consider non-negotiable should be pinned, not ranked** — `lessons add … --pin` puts it ahead of
164
+ every scored lesson ([/decisions/ad-043.md](/decisions/ad-043.md)).
165
+
166
+ `tlc harness doctor` carries the same facts as one row (`lesson health`), and warns separately about stale,
167
+ out-of-window and unproven lessons. See [/lessons.md](/lessons.md).
168
+
169
+ ## Two agents in one checkout, and the grind
170
+
171
+ A turn no longer blocks because a neighbour session is mid-gate. It resolves in this order
172
+ ([/decisions/ad-073.md](/decisions/ad-073.md)):
173
+
174
+ 1. **A recorded verdict whose inputs hash matches is reused**, and the lock is never taken. Two sessions editing
175
+ one tree usually land here, because the hash covers the command and the files rather than the session.
176
+ 2. **Otherwise the turn waits**, up to ten seconds — bounded so the wait plus the gate still fit inside the
177
+ `Stop` hook's 120-second timeout.
178
+ 3. **If the wait expires the gate defers.** The turn ends, the handoff records `last_gate_result: skipped` naming
179
+ the holder, and a `gate.outcome` carrying `deferred_to` reaches the record, so `tlc harness why` shows it.
180
+
181
+ Deferring is safe because both sessions share the tree: the neighbour's commands cover this turn's edits too. If
182
+ they pass, this turn was legitimate. If they fail, the neighbour is blocked holding the failure and the tree is
183
+ still broken, so the next stop in either session blocks on it. What is given up is only *which* turn is told.
184
+
185
+ A dead or stale holder never causes even the wait: the runtime reclaims past `GATE_LOCK_STALE_MS` (30 minutes) by
186
+ age, immediately when the pid is gone, and after a five-second grace window when the body cannot be read or names
187
+ no holder — a truncated write, a zero-length file, or JSON without `provider` / `session` / `pid`. Deleting the
188
+ file by hand is never necessary.
189
+
190
+ **An agent cannot switch the grind off from inside a session**, and that is correct: those subcommands are policy
191
+ surface ([/decisions/ad-022.md](/decisions/ad-022.md)). Run them from your own terminal.
192
+
193
+ ## `status` disagrees with what a hook does
194
+
195
+ It no longer can: `status` reads `loadPolicy`, the same resolution a hook performs, and prints where the mode
196
+ came from — `[from config]`, `[from file]` or `[from flag]`. If the origin is `flag` or `file` and you expected
197
+ `config`, a leftover `tlc harness mode` or `grind on` is winning; clear it with `tlc harness mode solo` or
198
+ `tlc harness grind off`.
199
+
200
+ ## A shim hook points at a path that does not exist
201
+
202
+ `tlc harness init` writes the install path (`~/.tlc/harness/bin/tlc-exec.mjs`), not the checkout behind its
203
+ symlink. A shim naming a checkout directory was written by an older runtime — re-run `tlc harness init` to
204
+ regenerate it. These files stay untracked on purpose: they carry an absolute path that is only valid on the
205
+ machine that generated them.
206
+
207
+ ## Doctor says a provider is "detected but not wired" while hooks fire
208
+
209
+ The launcher path is compared by the file it resolves to, not by the string. If this warning appears while
210
+ hooks demonstrably run, check that the path recorded in the provider's config still exists — a moved or
211
+ deleted checkout is a real break, whereas reaching the same file through a symlink is not and no longer
212
+ warns.
213
+
214
+ ## Grind not looping
215
+
216
+ 1. `tlc harness status` — grind must be ON.
217
+ 2. Gates must not be PAUSED.
218
+ 3. Project `.tlc/harness/config.json` needs `grind.lintCommand` / `grind.testCommand` if you expect those
219
+ gates.
220
+ 4. On failure, inspect `.tlc/harness/state/last-gate.json` (`findings`, `exitCode`, `outputTail`) before
221
+ trusting chat follow-up text.
222
+ 5. Concurrent agents: wait for `.tlc/harness/state/grind.lock` or stop the other grind.
223
+ 6. Stop status must be `completed` (aborted/error skips).
224
+
225
+ ## Subagent model denied
226
+
227
+ Allowlist + blocked `*-fast`-shaped patterns (provider-specific — see
228
+ [/providers/index.md](/providers/index.md)). Check `subagents.allowedModels` in user config and project
229
+ `.tlc/harness/config.json`. Dual gate: `subagentStart` + `preToolUse` on a spawn tool. Optional
230
+ `subagents.blockParentFast` denies spawns while sticky parent state is Fast
231
+ (`.tlc/harness/state/parent-model.json`, see [/decisions/ad-001.md](/decisions/ad-001.md)).
232
+
233
+ ## Cost always null
234
+
235
+ 1. `tlc harness help prices`.
236
+ 2. `tlc harness prices refresh` (or `refresh cursor` if you only need the primary catalog).
237
+ 3. `tlc harness prices lookup <model> [provider]` — if null, the id is missing from that provider's
238
+ catalog + LiteLLM.
239
+ 4. Add an alias in `~/.tlc/harness/model-aliases.json` if the provider's model slug ≠ catalog key.
240
+ 5. Optional override in `model-prices.json` (local).
241
+ 6. Events need input/output token counts; duration-only events yield null USD even when the catalog has the
242
+ model.
243
+
244
+ ## Double hooks / slow turns
245
+
246
+ If both user and project hooks run the same heavy logic without shim no-op, fix shim / `TLC_ACTIVE`. Global
247
+ observability hooks should stay in the user-level hook file only (the resolved provider config
248
+ directory's `hooks.json` or `settings.json`), not duplicated into the project shim.
package/docs/index.md ADDED
@@ -0,0 +1,52 @@
1
+ ---
2
+ okf_version: "0.1"
3
+ title: "agent-harness documentation bundle"
4
+ description: "OKF v0.1 documentation bundle for the tlc harness — architecture, concepts, runbooks, provider adapters, and the full architectural decision log."
5
+ tags: [index, okf]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # agent-harness docs
10
+
11
+ This is an [Open Knowledge Format v0.1](/decisions/ad-013.md) bundle: every non-reserved document below
12
+ carries YAML frontmatter with a `type` from the closed vocabulary `Concept | Runbook | Provider | Decision
13
+ | Capability | Aggregate`, plus `title`, `description`, `tags`, and `timestamp`. Cross-links are absolute
14
+ and bundle-relative (e.g. `/decisions/ad-010.md`), never relative.
15
+
16
+ See [/log.md](/log.md) for every decision in date order, and [/decisions/index.md](/decisions/index.md) for
17
+ the same records by number. Both are rendered from `docs/decisions/`, so neither can drift from it.
18
+
19
+ ## Concepts
20
+
21
+ | Doc | Description |
22
+ | --- | --- |
23
+ | [/architecture.md](/architecture.md) | Ports-and-adapters shape of the harness: contracts, core, providers, entrypoints, and how the tlc CLI and runtime home fit together. |
24
+ | [/concepts.md](/concepts.md) | The operator-facing concepts behind the harness: grind, pause/resume, shipGate, subagent allowlist, comment policy, duplication, catastrophic shell, shell stall, the intelligence rails, observability planes, and cost estimates. |
25
+ | [/coverage.md](/coverage.md) | A self-assessment against a published agentic-risk taxonomy: which of the harness's own rules answer each risk, and what each one still leaves open. Control names are generated from the catalogs and checked by the gate. |
26
+ | [/lessons.md](/lessons.md) | Durable, ranked lessons that keep the agent from repeating gate failures — three tiers, staleness against a named reference, a validity window, effectiveness measured after injection, lifecycle, config, ranking, and the per-provider rendered view. |
27
+
28
+ ## Runbooks
29
+
30
+ | Doc | Description |
31
+ | --- | --- |
32
+ | [/init.md](/init.md) | What tlc harness init creates, what it deliberately does not do, and the mandatory harness-init wizard's capability menu — including the Bun-vs-Node hook runtime question. |
33
+ | [/troubleshooting.md](/troubleshooting.md) | 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. |
34
+ | [/diagnose.md](/diagnose.md) | Checklist for hooks not firing, Node vs Bun runtime confusion, stale runtime, subagent denials, cost showing null, and double hooks — for both Cursor and Claude Code. |
35
+ | [/measure.md](/measure.md) | How to read harness observability: status, live signal, raw signal, session reports, audit trail, price catalogs, and the on-disk project state files, all provider-tagged. |
36
+
37
+ ## Providers
38
+
39
+ | Doc | Description |
40
+ | --- | --- |
41
+ | [/providers/index.md](/providers/index.md) | Index of the provider adapters — Cursor and Claude Code — and the port they both implement. |
42
+ | [/providers/cursor.md](/providers/cursor.md) | The Cursor adapter — capability descriptor, event mapping, and wiring target for Cursor's hooks.json. |
43
+ | [/providers/claude-code.md](/providers/claude-code.md) | The Claude Code adapter — capability descriptor, event mapping, and wiring target for the settings.json hooks block in Claude Code's resolved config directory. |
44
+
45
+ ## Decisions
46
+
47
+ | Doc | Description |
48
+ | --- | --- |
49
+ | [/decisions/index.md](/decisions/index.md) | Index of every architectural decision (AD-001…AD-016) made while building the multi-provider harness. |
50
+
51
+ Each individual decision (`/decisions/ad-001.md` … `/decisions/ad-016.md`) is listed with its own
52
+ description in [/decisions/index.md](/decisions/index.md).
package/docs/init.md ADDED
@@ -0,0 +1,101 @@
1
+ ---
2
+ type: Runbook
3
+ title: "Init (project bootstrap)"
4
+ description: "What tlc harness init creates, what it deliberately does not do, and the mandatory harness-init wizard's capability menu — including the Bun-vs-Node hook runtime question."
5
+ tags: [runbook, init, wizard]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # Init (project bootstrap)
10
+
11
+ ## What init creates (project-agnostic)
12
+
13
+ | File | Purpose |
14
+ |------|---------|
15
+ | `.tlc/harness/config.json` | Policy for this repo only |
16
+ | `.cursor/hooks.json` | Cursor shim to the global runtime — only written when `~/.cursor` is detected |
17
+ | `.claude/settings.json` (`hooks` block, merged) | Claude Code shim — only written when `~/.claude` is detected |
18
+ | `.gitignore` entries | `.tlc/harness/state/` |
19
+
20
+ `tlc harness init` detects which providers are installed (presence of `~/.cursor`, `~/.claude`) and wires
21
+ only those — it never assumes Cursor. See `tools/init-project.ts` and
22
+ [/providers/index.md](/providers/index.md).
23
+
24
+ ## What init does NOT do
25
+
26
+ - Does not install the global runtime (assumes `~/.tlc/harness` already present)
27
+ - Does not assume Biome, Vitest, npm, or any stack
28
+ - Does not enable grind/shipGate/etc. unless the user opts in during discovery
29
+
30
+ ## Wizard rule
31
+
32
+ The **harness-init skill is mandatory** for project setup. **Each capability is optional** — the wizard
33
+ must present benefit / trade-off / default and ask before enabling.
34
+
35
+ ## Step 1b: hook runtime (ask once, never block)
36
+
37
+ Before the capability menu, the wizard checks whether **Bun** is on `PATH` and presents the measured
38
+ trade-off from [/decisions/ad-012.md](/decisions/ad-012.md):
39
+
40
+ ```
41
+ Hook runtime: Node (Bun not found)
42
+ Bun runs each hook in ~1ms; Node needs ~27ms.
43
+ At ~30 hooks per turn that is ~30ms vs ~810ms of startup.
44
+ Install: curl -fsSL https://bun.sh/install | bash
45
+ Node works fine — just slower. Continue on Node?
46
+ ```
47
+
48
+ Rules: never block (Node is fully supported), never ask twice (the answer is recorded in project state so
49
+ later runs skip this step; `doctor` keeps reporting it as a non-failing `WARN`), always give the exact
50
+ numbers, and re-probe if the user installs Bun mid-wizard rather than trusting the earlier check.
51
+
52
+ ## Capability menu (for the skill wizard)
53
+
54
+ | Capability | Keys | Default | Benefit | Trade-off |
55
+ |------------|------|---------|---------|-----------|
56
+ | format | `format.*` | off | Consistent style on Write | Needs a correct format command |
57
+ | grind | `grind.*` | off | Lint/test follow-ups on stop | Turn cost; flaky commands thrash |
58
+ | shipGate | `shipGate.*` + `HARNESS_SHIP_CLAIM` | off | Honest ship after protocol claim | Needs evidence workflow; free English ignored |
59
+ | emptyDiffAntiShip | `shipGate.emptyDiffAntiShip` | off | No claim on empty tree | Annoys when zero-diff is correct |
60
+ | comments | `comments.*` | off | Junk-comment follow-ups | Noise on dirty trees |
61
+ | subagents | `subagents.*` | off | Model allowlist / require model | Maintain the list |
62
+ | blockParentFast | `subagents.blockParentFast` | off | Deny Task/subagent while parent is Fast | Needs sticky parent hooks; blocks intentional Fast parent |
63
+ | catastrophic shell | `shell.catastrophicAsk` | on | Ask before destructive shell | Extra prompts |
64
+ | shell stall | `shell.stallDetection` | off | Block repeated identical commands | False positives on retries |
65
+ | gap feedback | `intelligence.gapFeedback` | on | Structured PREVIOUS_GAPS | Longer follow-ups |
66
+ | failure classification | `intelligence.failureClassification` | on | Failure categories in handoff | Extra fields |
67
+ | progressive handoff | `intelligence.progressiveHandoff` | on | Gaps injected on next session | Slightly longer bootstrap |
68
+ | progressive context | `intelligence.progressiveContext` | on | Escalates context on each stop retry | Longer follow-ups on thrash |
69
+ | autopilot | `intelligence.autopilot` | on | Runtime decides ordered next steps | Agent must follow AUTOPILOT block |
70
+ | lessons | `intelligence.lessons.*` | off | Durable ranked lessons + decay | Uses context; enable when you want cross-session memory of gate fails |
71
+ | budget continue | `intelligence.budgetContinue` | off | Keep working under pressure if unfinished | Can delay clean stops |
72
+ | observability | usually global | signal on | Measure/diagnose | Disk under `.tlc/harness/state/` |
73
+ | mcpPrime / bootstrapExtra | arrays | empty | Project rails at sessionStart | Context cost |
74
+
75
+ Stagnation fingerprinting is built into grind gate fails (no separate toggle).
76
+
77
+ ## CLI flags
78
+
79
+ ```bash
80
+ tlc harness init --dry-run
81
+ tlc harness init --write [--stdin-json] [--force]
82
+ tlc harness init --minimal
83
+ ```
84
+
85
+ `--minimal` writes a safe, provider-agnostic stub (grind/ship off) plus whichever provider shims are
86
+ detected. Prefer the harness-init skill for full discovery.
87
+
88
+ ## After write
89
+
90
+ ```bash
91
+ tlc harness status
92
+ tlc harness doctor
93
+ ```
94
+
95
+ Next agent turn should set `TLC_ACTIVE` via the global `sessionStart` hook. Use `tlc harness help` for
96
+ concepts.
97
+
98
+ ## Slash / global commands
99
+
100
+ Keep provider-native slash commands (e.g. Cursor's `~/.cursor/commands/`) global. Init must not create
101
+ `<repo>/.cursor/commands/` or an equivalent project-local commands directory for harness toggles or help.