@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,418 @@
1
+ {
2
+ "catalogVersion": 13,
3
+ "capabilities": [
4
+ {
5
+ "id": "grind",
6
+ "configPath": "grind.enabled",
7
+ "title": "Grind (lint/test on stop)",
8
+ "summary": "Runs your lint and test commands against the files the turn changed, and sends the agent back until they pass.",
9
+ "benefit": "Re-checks lint/test after each completed turn and follow-ups until gates pass.",
10
+ "tradeOff": "Uses turns; flaky commands thrash the agent. The gate's cost is paid once per attempt, up to maxLoops — a four-minute suite with maxLoops 5 can spend twenty minutes on tests alone. tlc harness obs report shows the runs and the total.",
11
+ "defaultOn": false,
12
+ "sinceCatalogVersion": 1,
13
+ "fires": [
14
+ "stop"
15
+ ],
16
+ "verdict": "follow-up",
17
+ "inspect": "tlc harness obs report — runs, wall-clock and total; the last verdict is in the project state directory as last-gate.json",
18
+ "asks": [
19
+ "lintCommand",
20
+ "testCommand",
21
+ "maxLoops"
22
+ ]
23
+ },
24
+ {
25
+ "id": "shipGate",
26
+ "configPath": "shipGate.enabled",
27
+ "title": "Ship gate",
28
+ "summary": "Checks a declared ship claim against recent PASS evidence for the runtime paths the turn touched.",
29
+ "benefit": "Blocks false done after an explicit HARNESS_SHIP_CLAIM when evidence is missing.",
30
+ "tradeOff": "Needs a real evidenceDir workflow; free-English done is ignored.",
31
+ "defaultOn": false,
32
+ "sinceCatalogVersion": 1,
33
+ "fires": [
34
+ "stop"
35
+ ],
36
+ "verdict": "block-stop",
37
+ "inspect": "tlc harness obs report; the ship ledger in the project state directory records every claim, challenge and pass",
38
+ "asks": [
39
+ "evidenceDir",
40
+ "runtimePathPrefixes",
41
+ "runtimePathExcludes",
42
+ "evidenceMaxAgeHours",
43
+ "claimWindowMinutes"
44
+ ]
45
+ },
46
+ {
47
+ "id": "emptyDiffAntiShip",
48
+ "configPath": "shipGate.emptyDiffAntiShip",
49
+ "title": "Empty-diff anti-ship",
50
+ "summary": "Checks that a ship claim has a non-empty diff behind it.",
51
+ "benefit": "Blocks a ship claim when the working tree has zero changes.",
52
+ "tradeOff": "Annoys when a zero-diff claim is intentionally correct.",
53
+ "defaultOn": false,
54
+ "sinceCatalogVersion": 1,
55
+ "fires": [
56
+ "stop"
57
+ ],
58
+ "verdict": "block-stop",
59
+ "inspect": "the ship ledger in the project state directory — the challenge row names the empty diff",
60
+ "asks": [
61
+ "requires shipGate enabled"
62
+ ]
63
+ },
64
+ {
65
+ "id": "comments",
66
+ "configPath": "comments.enabled",
67
+ "title": "Comment gate (agent-added comments)",
68
+ "summary": "Checks the comment lines this turn added against the commit the turn started from: by reason, by resolvability, or none.",
69
+ "benefit": "Blocks the stop when this turn added comment lines, so narration never lands. Diff-scoped: comments you already committed are never flagged.",
70
+ "tradeOff": "Three modes. declared lets the agent keep a comment by writing why:/hazard:/invariant:; resolvable adds the question the marker cannot answer — can a reader who was not in the session check this? — and catches change narration, dead plan or decision citations, and comments arguing with a reviewer; strict accepts none and asks you to write it instead. Each is stricter and interrupts more.",
71
+ "defaultOn": false,
72
+ "sinceCatalogVersion": 1,
73
+ "fires": [
74
+ "stop"
75
+ ],
76
+ "verdict": "block-stop",
77
+ "inspect": "tlc harness obs report — the comments gate appears among the gate outcomes",
78
+ "asks": [
79
+ "mode: declared | resolvable | strict"
80
+ ]
81
+ },
82
+ {
83
+ "id": "duplication",
84
+ "configPath": "duplication.enabled",
85
+ "title": "Duplication gate (agent-added copies)",
86
+ "summary": "Checks whether the runs of code this turn added already exist somewhere else in the project.",
87
+ "benefit": "Blocks the stop when the turn wrote six or more lines the project already has, naming both sites. Diff-scoped: what was already duplicated is not counted.",
88
+ "tradeOff": "Reads every tracked file on stop, bounded at 2000 files and 8 MB, and says when a bound was reached. Comments, dependency declarations and pure data — object literals, type bodies, export lists — are excluded, so it reports duplicated logic and misses duplicated shape.",
89
+ "defaultOn": false,
90
+ "sinceCatalogVersion": 11,
91
+ "fires": [
92
+ "stop"
93
+ ],
94
+ "verdict": "block-stop",
95
+ "inspect": "tlc harness obs report — the duplication gate appears among the gate outcomes",
96
+ "asks": [
97
+ "minRun: lines a run must reach before two copies count (default 6)"
98
+ ]
99
+ },
100
+ {
101
+ "id": "supplyChain",
102
+ "configPath": "supplyChain.enabled",
103
+ "title": "Supply-chain gate (dependencies this turn added)",
104
+ "summary": "Checks what this turn added to the dependency graph: a manifest that moved without its lock, or an unpinned version.",
105
+ "benefit": "Blocks the stop when a dependency added this turn is not recorded in a lockfile, or is specified as latest/*/no version. A dependency added in a turn runs on every later turn, in CI, and on every machine that installs the project.",
106
+ "tradeOff": "Recognises a manifest by filename from one table, so an ecosystem the table does not carry is not covered. For a JSON manifest the declared dependency sections decide, so it reads the manifest as it stands — without that, a rename or a scripts entry reads as a dependency. It does not check advisories, licences or typosquats: those need the network on every stop.",
107
+ "defaultOn": false,
108
+ "sinceCatalogVersion": 12,
109
+ "fires": [
110
+ "stop"
111
+ ],
112
+ "verdict": "block-stop",
113
+ "inspect": "tlc harness obs report — the supply-chain gate appears among the gate outcomes",
114
+ "asks": []
115
+ },
116
+ {
117
+ "id": "subagents",
118
+ "configPath": "subagents.enforceAllowlist",
119
+ "title": "Subagent allowlist",
120
+ "summary": "Checks a subagent's model against the list you wrote, and against the blocked *-fast shapes.",
121
+ "benefit": "Restricts Task/subagent models to a list you write, and blocks *-fast shapes by default.",
122
+ "tradeOff": "The harness ships no model list, so switching this on without filling allowedModels enforces nothing and doctor reports it as a fault. You maintain the list as providers add models — including a value for `inherit` if you want subagents to take the parent's model.",
123
+ "defaultOn": false,
124
+ "sinceCatalogVersion": 1,
125
+ "fires": [
126
+ "tool.before",
127
+ "subagent.start"
128
+ ],
129
+ "verdict": "deny",
130
+ "inspect": "tlc harness obs report — refusals attributed by rule; the denial text names subagents.allowedModels and lists what is permitted",
131
+ "asks": [
132
+ "allowedModels",
133
+ "requireModel",
134
+ "blockMode"
135
+ ]
136
+ },
137
+ {
138
+ "id": "blockParentFast",
139
+ "configPath": "subagents.blockParentFast",
140
+ "title": "Block parent Fast mode for Task spawns",
141
+ "summary": "Checks whether the parent chat is in Fast mode before letting it spawn a subagent.",
142
+ "benefit": "Denies Task/subagentStart while the parent chat is in Fast mode (sticky from hooks), closing the gap where Task slugs omit *-fast.",
143
+ "tradeOff": "Needs parent model hooks (sessionStart/obs/stop); false denials if you intentionally run Fast parent with workers.",
144
+ "defaultOn": false,
145
+ "sinceCatalogVersion": 1,
146
+ "fires": [
147
+ "tool.before",
148
+ "subagent.start"
149
+ ],
150
+ "verdict": "deny",
151
+ "inspect": "tlc harness obs report — refusals by rule; tlc harness status shows the sticky parent model it read",
152
+ "recommend": "on"
153
+ },
154
+ {
155
+ "id": "shellStall",
156
+ "configPath": "shell.stallDetection",
157
+ "title": "Shell stall detection",
158
+ "summary": "Counts identical shell commands in a row and stops the loop at your threshold.",
159
+ "benefit": "Blocks repeating the exact same shell command too many times.",
160
+ "tradeOff": "Can false-positive on intentional retries.",
161
+ "defaultOn": false,
162
+ "sinceCatalogVersion": 1,
163
+ "fires": [
164
+ "shell.before"
165
+ ],
166
+ "verdict": "deny",
167
+ "inspect": "tlc harness obs report — interruptions attributed to the shell-stall rule",
168
+ "asks": [
169
+ "stallRepeatThreshold (default 3)"
170
+ ]
171
+ },
172
+ {
173
+ "id": "catastrophicShell",
174
+ "configPath": "shell.catastrophicAsk",
175
+ "title": "Catastrophic shell ask",
176
+ "summary": "Checks a shell command for destruction that reaches outside the workspace.",
177
+ "benefit": "Asks before destructive shell commands (rm -rf, drop db, force push, …).",
178
+ "tradeOff": "Extra prompts on risky commands.",
179
+ "defaultOn": true,
180
+ "sinceCatalogVersion": 1,
181
+ "fires": [
182
+ "shell.before"
183
+ ],
184
+ "verdict": "ask",
185
+ "inspect": "tlc harness obs report — interruptions attributed to the shell-catastrophic rule",
186
+ "recommend": "on"
187
+ },
188
+ {
189
+ "id": "lessons",
190
+ "configPath": "intelligence.lessons.enabled",
191
+ "title": "Lessons",
192
+ "summary": "Records what a repeated gate failure taught, ranks it, and injects it into the next session and retry.",
193
+ "benefit": "Records compact lessons on gate stagnation and reinjects them ranked under a char budget. A lesson can name the path or symbol that makes it true and is withheld once that stops resolving, can carry an end date, and is graded helped or neutral by the next run of the gate it was injected for. Three tiers: shipped core, a global tier read by every product on this machine, and this project's own. How the lessons reach the model is decided by the provider rather than configured: where a host does not deliver context returned from its session-start hook, a durable rules file is written instead.",
194
+ "tradeOff": "Uses context tokens; not a second brain / chat memory. The grading is correlational, not causal — a gate passing after a lesson was injected does not prove the lesson caused it. Nothing is promoted between products automatically, so carrying a lesson to another product is an operator command. On a host that needs the durable view, a file is written into the repo (`.cursor/rules/harness-lessons.mdc`) and asked to be included on every request; `syncRulesFile: never` declines it.",
195
+ "defaultOn": false,
196
+ "sinceCatalogVersion": 1,
197
+ "fires": [
198
+ "session.start",
199
+ "stop",
200
+ "session.end"
201
+ ],
202
+ "verdict": "context",
203
+ "inspect": "tlc harness lessons list — every tier with staleness and effectiveness; obs report shows the characters each injection cost",
204
+ "asks": [
205
+ "maxInjectSession",
206
+ "maxCharsSession",
207
+ "maxInjectRetry",
208
+ "maxCharsRetry",
209
+ "promoteHitCount",
210
+ "decayLambda",
211
+ "projectBoost",
212
+ "syncRulesFile",
213
+ "gardenOnSessionEnd"
214
+ ],
215
+ "recommend": "on"
216
+ },
217
+ {
218
+ "id": "budgetContinue",
219
+ "configPath": "intelligence.budgetContinue",
220
+ "title": "Budget continue",
221
+ "summary": "Checks for unfinished handoff work under context pressure and says keep going rather than wrap up.",
222
+ "benefit": "Pushes the agent to keep working under context pressure instead of wrapping up early.",
223
+ "tradeOff": "Can delay clean stops.",
224
+ "defaultOn": false,
225
+ "sinceCatalogVersion": 1,
226
+ "fires": [
227
+ "stop"
228
+ ],
229
+ "verdict": "follow-up",
230
+ "inspect": "tlc harness handoff — the follow-up fires only with unfinished work recorded there",
231
+ "asks": [
232
+ "budgetContinueAfterLoops"
233
+ ]
234
+ },
235
+ {
236
+ "id": "gapFeedback",
237
+ "configPath": "intelligence.gapFeedback",
238
+ "title": "Gap feedback",
239
+ "summary": "Turns a gate's output into a numbered list of gaps the retry has to close.",
240
+ "benefit": "Injects PREVIOUS_GAPS on gate failure so retries fix listed items.",
241
+ "tradeOff": "Longer follow-ups.",
242
+ "defaultOn": true,
243
+ "sinceCatalogVersion": 1,
244
+ "fires": [
245
+ "stop"
246
+ ],
247
+ "verdict": "follow-up",
248
+ "inspect": "tlc harness handoff — the gaps it injects are the ones stored as previous_gaps"
249
+ },
250
+ {
251
+ "id": "failureClassification",
252
+ "configPath": "intelligence.failureClassification",
253
+ "title": "Failure classification",
254
+ "summary": "Labels each gate failure with a category and stores it on the handoff.",
255
+ "benefit": "Stores failure categories on the handoff for clearer next actions.",
256
+ "tradeOff": "Extra handoff fields.",
257
+ "defaultOn": true,
258
+ "sinceCatalogVersion": 1,
259
+ "fires": [
260
+ "stop"
261
+ ],
262
+ "verdict": "record",
263
+ "inspect": "tlc harness handoff — last_failure_category"
264
+ },
265
+ {
266
+ "id": "progressiveHandoff",
267
+ "configPath": "intelligence.progressiveHandoff",
268
+ "title": "Progressive handoff",
269
+ "summary": "Reads the gaps the previous session ended with back out at the next session's start.",
270
+ "benefit": "Carries the gaps the previous session ended with into the next session's bootstrap, as history rather than as a task list.",
271
+ "tradeOff": "Spends session-start context on a verdict that may already be stale — only the next run of the gate says whether it still holds. Capped at five, and the rest are counted rather than dropped in silence.",
272
+ "defaultOn": true,
273
+ "sinceCatalogVersion": 1,
274
+ "fires": [
275
+ "session.start"
276
+ ],
277
+ "verdict": "context",
278
+ "inspect": "tlc harness handoff — the gaps it reads back out are previous_gaps"
279
+ },
280
+ {
281
+ "id": "progressiveContext",
282
+ "configPath": "intelligence.progressiveContext",
283
+ "title": "Progressive context",
284
+ "summary": "Raises the detail in the follow-up on each stop retry, so a repeat attempt is not given the same prompt.",
285
+ "benefit": "Escalates gate follow-up detail on each stop retry.",
286
+ "tradeOff": "Longer thrash follow-ups.",
287
+ "defaultOn": true,
288
+ "sinceCatalogVersion": 1,
289
+ "fires": [
290
+ "stop"
291
+ ],
292
+ "verdict": "follow-up",
293
+ "inspect": "tlc harness obs report — the retry count for a stop is the escalation level it reached"
294
+ },
295
+ {
296
+ "id": "autopilot",
297
+ "configPath": "intelligence.autopilot",
298
+ "title": "Autopilot",
299
+ "summary": "Emits ordered steps after a gate failure, computed by the runtime rather than invented by the model.",
300
+ "benefit": "Adds ordered AUTOPILOT steps on gate failure.",
301
+ "tradeOff": "Agent must follow the block; more directive follow-ups.",
302
+ "defaultOn": true,
303
+ "sinceCatalogVersion": 1,
304
+ "fires": [
305
+ "stop"
306
+ ],
307
+ "verdict": "follow-up",
308
+ "inspect": "the AUTOPILOT block is in the follow-up text itself; obs report counts the failing stops that produced one"
309
+ },
310
+ {
311
+ "id": "idleTurnGate",
312
+ "configPath": "intelligence.idleTurnGate",
313
+ "title": "Idle-turn gate (asked instead of acting)",
314
+ "summary": "Checks whether a turn that ended with open work recorded any tool call or file change at all.",
315
+ "benefit": "Blocks a turn that ends with open work, zero tool calls and zero file changes. Counts recorded tool events rather than reading the reply, so it cannot be talked around.",
316
+ "tradeOff": "A turn that legitimately only answers a question is blocked while handoff work is open — clear the handoff or turn this off for conversational repos.",
317
+ "defaultOn": false,
318
+ "sinceCatalogVersion": 3,
319
+ "fires": [
320
+ "stop"
321
+ ],
322
+ "verdict": "block-stop",
323
+ "inspect": "tlc harness obs report for the block; tlc harness handoff shows the open work that armed it"
324
+ },
325
+ {
326
+ "id": "docsGate",
327
+ "configPath": "docs.command",
328
+ "title": "Docs staleness gate",
329
+ "summary": "Runs the repository's own documentation staleness tool on stop, like a lint command.",
330
+ "benefit": "Runs the repository's own documentation staleness tool on stop, so a stale document fails like a failing test.",
331
+ "tradeOff": "Needs such a tool in the repository; without one there is nothing to run.",
332
+ "defaultOn": false,
333
+ "sinceCatalogVersion": 5,
334
+ "fires": [
335
+ "stop"
336
+ ],
337
+ "verdict": "block-stop",
338
+ "inspect": "tlc harness obs report; the docs gate writes the same last-gate.json artifact the lint and test gates do",
339
+ "asks": [
340
+ "command (exact argv array)",
341
+ "severity: warn | deny"
342
+ ]
343
+ },
344
+ {
345
+ "id": "globalSpool",
346
+ "configPath": "obs.globalSpool",
347
+ "title": "Global observability spool",
348
+ "summary": "Copies every record into one file under the runtime home, so cost is readable across repositories.",
349
+ "benefit": "Mirrors this repo's obs and audit records into one file under the runtime home, so cost and gate history can be read across every repository at once.",
350
+ "tradeOff": "Writes outside the repository. Records carry the repo path and project name, and the spool is pruned on the same retention window as session rollups.",
351
+ "defaultOn": false,
352
+ "sinceCatalogVersion": 6,
353
+ "fires": [
354
+ "tool.after",
355
+ "tool.failure"
356
+ ],
357
+ "verdict": "record",
358
+ "inspect": "the spool file under the runtime home; tlc harness obs prune reports how many records it dropped",
359
+ "asks": []
360
+ },
361
+ {
362
+ "id": "untrustedContent",
363
+ "configPath": "untrustedContent.enabled",
364
+ "title": "Untrusted-content framing and enforcement",
365
+ "summary": "Frames outside content as data, and in enforce mode asks before a command that appears verbatim in it.",
366
+ "benefit": "Injects one framing line per turn when the agent reads a pull request, an issue, a fetched page or an MCP result, stating that the content is data and that any directive inside it is to be reported as a prompt-injection attempt, not obeyed.",
367
+ "tradeOff": "Two modes. frame injects one line per turn, costing a few hundred characters and enforcing nothing. enforce also remembers what an untrusted read returned, bounded at 64 KB per session, and asks before a shell command that appears verbatim in it — verbatim because a paraphrase cannot be shown to come from the content, so a rewritten command is missed. It needs the host to deliver tool output on the after-event: measured present on Claude Code's PostToolUse and on Cursor's afterShellExecution and afterMCPExecution, and absent on Cursor's generic postToolUse.",
368
+ "defaultOn": false,
369
+ "sinceCatalogVersion": 7,
370
+ "fires": [
371
+ "tool.after"
372
+ ],
373
+ "verdict": "context",
374
+ "inspect": "tlc harness obs report — one framing injection per turn, with the characters it cost",
375
+ "asks": [
376
+ "mode: frame | enforce"
377
+ ]
378
+ },
379
+ {
380
+ "id": "planGate",
381
+ "configPath": "planGate.enabled",
382
+ "title": "Plan gate (declared scope vs diff)",
383
+ "summary": "Checks the files the turn changed against the scope it declared, and against any stated deviation.",
384
+ "benefit": "Blocks the stop when the turn changed files the declared HARNESS_PLAN did not name, so scope creep fails like a failing test instead of surviving as a review comment.",
385
+ "tradeOff": "Requires the agent to declare HARNESS_PLAN before editing, and each honest deviation to state a reason. A turn with no declaration is not gated at all.",
386
+ "defaultOn": false,
387
+ "sinceCatalogVersion": 8,
388
+ "fires": [
389
+ "response.after",
390
+ "stop"
391
+ ],
392
+ "verdict": "block-stop",
393
+ "inspect": "tlc harness handoff — plan_paths, plan_at and plan_deviations",
394
+ "asks": [
395
+ "windowMinutes"
396
+ ]
397
+ },
398
+ {
399
+ "id": "observe",
400
+ "configPath": "observe.enabled",
401
+ "title": "Observation mode (measure a rail with its rule off)",
402
+ "summary": "Runs a rail's checker while that rail is not enforcing, and records the reading without acting on it.",
403
+ "benefit": "Runs a rail's checker while that rail is not enforcing, so the record says whether the property held with the rule injected or without it. That is the reading that tells you a rail is unnecessary rather than merely quiet, and it is what makes deleting one a decision instead of a guess.",
404
+ "tradeOff": "Costs one diff scan per turn per observed rail, and answers a question only an operator who is asking it needs answered. It never blocks and never changes a decision, so it buys information and nothing else.",
405
+ "defaultOn": false,
406
+ "sinceCatalogVersion": 9,
407
+ "fires": [
408
+ "stop",
409
+ "session.end"
410
+ ],
411
+ "verdict": "record",
412
+ "inspect": "tlc harness obs report — the observation readings, held apart from the refusal counters so those stay honest",
413
+ "asks": [
414
+ "rails — the rails to observe, chosen from the observable set (today: comments). A name with no checker records nothing and doctor reports it"
415
+ ]
416
+ }
417
+ ]
418
+ }
@@ -0,0 +1,51 @@
1
+ {
2
+ "version": 1,
3
+ "mode": "solo",
4
+ "subagents": {
5
+ "enforceAllowlist": true,
6
+ "requireModel": true,
7
+ "blockMode": "deny",
8
+ "blockParentFast": false,
9
+ "readOnlyTypes": [
10
+ "explore"
11
+ ]
12
+ },
13
+ "docs": {
14
+ "command": null,
15
+ "severity": "warn"
16
+ },
17
+ "comments": {
18
+ "enabled": true,
19
+ "onViolation": "followup",
20
+ "mode": "declared"
21
+ },
22
+ "obs": {
23
+ "globalSpool": false,
24
+ "retentionDays": 14
25
+ },
26
+ "shell": {
27
+ "catastrophicAsk": true,
28
+ "stallDetection": true,
29
+ "stallRepeatThreshold": 3
30
+ },
31
+ "intelligence": {
32
+ "gapFeedback": true,
33
+ "failureClassification": true,
34
+ "progressiveHandoff": true,
35
+ "progressiveContext": true,
36
+ "autopilot": true,
37
+ "budgetContinue": false,
38
+ "budgetContinueAfterLoops": 3,
39
+ "lessons": {
40
+ "enabled": true,
41
+ "maxInjectSession": 5,
42
+ "maxInjectRetry": 8,
43
+ "maxCharsSession": 900,
44
+ "maxCharsRetry": 1400,
45
+ "promoteHitCount": 2,
46
+ "decayLambda": 0.02,
47
+ "projectBoost": 1.5,
48
+ "gardenOnSessionEnd": true
49
+ }
50
+ }
51
+ }