@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,51 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-002 — Provider-neutral naming and layout"
4
+ description: "The CLI binary is tlc with a harness subcommand group, the runtime home is ~/.tlc/harness/, and the repository is github.com/felipfr/tlc-agent-harness."
5
+ tags: [decision, naming, cli, layout]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-002 — Provider-neutral naming and layout
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ CLI binary is `tlc` with a `harness` subcommand group. Runtime home `~/.tlc/harness/`. Project source of
17
+ truth `.tlc/harness/config.json` (tracked) + `.tlc/harness/state/` (ignored). Repository is
18
+ `github.com/felipfr/tlc-agent-harness`; the predecessor keeps its own name.
19
+
20
+ > **The repository clause is superseded by [/decisions/ad-052.md](/decisions/ad-052.md)**: the project lives at
21
+ > `github.com/tech-leads-club/harness-toolkit`. Everything else in this decision — the binary, the runtime home and
22
+ > the project policy path — is unchanged, and AD-052 says why moving them was rejected.
23
+
24
+ ## Why
25
+
26
+ `harness` is the primary binary of Harness.io CLI 3.0 (public beta) — a PATH-order collision that fails
27
+ silently. `.tlc/` namespaces the suite so future `.tlc/skills/` needs no new root dir. A Claude-only user
28
+ must not install into `~/.cursor/`.
29
+
30
+ ## Applies to
31
+
32
+ - installers (`install.sh`, `install.ps1`)
33
+ - `bin/`
34
+ - all path resolution (`src/platform/paths.ts`)
35
+ - docs
36
+ - the harness-init skill
37
+
38
+ ## Repo hygiene — `.specs/` stays local
39
+
40
+ `.specs/` holds working specs and task ledgers for whoever is building, which is not part of the product
41
+ tree and goes stale against the code the moment a task lands. The durable *why* lives in this decision
42
+ bundle instead, which is versioned and validated by `tools/check-docs-bundle.ts`.
43
+
44
+ `.gitignore` also dropped the stale blanket `.cursor/` and `state/` entries (predecessor layout) in favour
45
+ of `.tlc/harness/state/`, since under this decision the provider adapters (`.cursor/hooks.json`, the
46
+ `hooks` block of `.claude/settings.json`) and the project policy are meant to be tracked.
47
+
48
+ ## See also
49
+
50
+ - [/decisions/ad-003.md](/decisions/ad-003.md) — no backward-compatible dual read of the old layout
51
+ - [/decisions/ad-006.md](/decisions/ad-006.md) — Windows path handling under the new layout
@@ -0,0 +1,30 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-003 — No backward compatibility"
4
+ description: "No dual-read of .cursor/harness*, no migrate command, no compatibility symlinks — single code path only."
5
+ tags: [decision, migration, layout]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-003 — No backward compatibility
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ No dual-read of `.cursor/harness*`, no migrate command, no compatibility symlinks. Single code path only.
17
+
18
+ ## Why
19
+
20
+ Two repos, single operator, hand-adjusted. Compatibility layers would be permanent dead weight.
21
+
22
+ ## Applies to
23
+
24
+ - path resolution
25
+ - config loading
26
+ - installers
27
+
28
+ ## See also
29
+
30
+ - [/decisions/ad-002.md](/decisions/ad-002.md) — the new layout this decision refuses to bridge to the old one
@@ -0,0 +1,37 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-004 — Ports and adapters with an anti-corruption layer per provider"
4
+ description: "Core steering logic is provider-agnostic; each provider is an ACL adapter exposing detect, capabilities, toEvent, render, and core degrades on a declarative capability descriptor, never on provider identity."
5
+ tags: [decision, architecture, providers, core]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-004 — Ports and adapters with an anti-corruption layer per provider
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ Core steering logic is provider-agnostic and organized flat-by-aggregate under `core/`. Each provider is an
17
+ ACL adapter under `providers/<name>/` exposing `detect`, `capabilities`, `toEvent`, `render`. Core degrades
18
+ on a declarative `ProviderCapabilities` descriptor, never on provider identity. Enforced by a registry
19
+ contract test plus a lint asserting zero vendor identifiers inside `core/`.
20
+
21
+ ## Why
22
+
23
+ The hook IO contract is the expensive coupling. A capability descriptor turns provider asymmetry into
24
+ data, which is what makes a hookless provider (all capabilities false → instruction rendering) a new file
25
+ rather than a refactor.
26
+
27
+ ## Applies to
28
+
29
+ - `src/core/`
30
+ - `src/providers/`
31
+ - all entrypoint handlers
32
+
33
+ ## See also
34
+
35
+ - [/architecture.md](/architecture.md) — the port shape in practice
36
+ - [/providers/index.md](/providers/index.md) — the two adapters that implement this port today
37
+ - [/decisions/ad-010.md](/decisions/ad-010.md) — where the shared vocabulary lives
@@ -0,0 +1,36 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-005 — Local test runner is the gate; CI matrix runs on every push"
4
+ description: "tlc harness test is the per-task gate; .github/workflows/ci.yml runs the ubuntu|macos|windows matrix on push and pull_request, free on standard runners because the repository is public."
5
+ tags: [decision, ci, gate, testing]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-005 — Local test runner is the gate; CI matrix runs on every push
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ `tlc harness test` runs the full suite (golden-file wire formats, registry contract, core-purity lint,
17
+ concurrency, platform units) and is the per-task gate. `.github/workflows/ci.yml` carries the
18
+ `ubuntu|macos|windows` matrix on `push` to main, on `pull_request`, and on demand.
19
+
20
+ ## Why
21
+
22
+ A task cannot be called done on inspection, so the gate must be runnable locally and must be the
23
+ per-task gate. CI is the second, independent pass, and the only place the Windows leg runs.
24
+
25
+ Standard GitHub-hosted runners are free and unlimited on public repositories, across Linux, macOS and
26
+ Windows. Larger runners bill even there, so the matrix stays on standard ones.
27
+
28
+ ## Applies to
29
+
30
+ - `.github/workflows/ci.yml`
31
+ - `bin/tlc-cli.ts`
32
+ - every task's definition of done
33
+
34
+ ## See also
35
+
36
+ - [/decisions/ad-008.md](/decisions/ad-008.md) — what the local gate actually runs
@@ -0,0 +1,49 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-006 — Windows ships in scope, with CI covering the suite and the build"
4
+ description: "Windows path resolution, exec-form hooks and the .cmd shim are in scope; the CI windows-latest leg runs the suite and the dist build; install.ps1 and the editor end sit outside CI coverage."
5
+ tags: [decision, windows, platform]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-006 — Windows ships in scope, with CI covering the suite and the build
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ Windows stays in scope for design and code (`os.homedir()` only, exec-form hooks, sanitized filenames,
17
+ atomic-write retry, junctions + `.cmd` shim). The CI `windows-latest` leg runs the suite and the `dist/`
18
+ build on every push. `install.ps1` and hooks firing inside an editor session sit outside that coverage
19
+ hardware.
20
+
21
+ ## Why
22
+
23
+ The portable choice is the correct choice independently of Windows, so deferring would relocate work rather
24
+ than remove it — and would reopen every call site later. But the operator develops on WSL and cannot prove
25
+ Windows behavior; claiming validation would be dishonest.
26
+
27
+ ## Amended 2026-08-03 — `install.sh` is covered on POSIX
28
+
29
+ "Installers sit outside coverage" was read as *no installer test at all*, and that gap shipped a broken
30
+ primary install path: `curl … | bash` aborted on `${BASH_SOURCE[0]}` under `set -u`, because a piped script has
31
+ no file on disk. The README's first command could not run on any platform, and nothing failed to say so.
32
+
33
+ `install.sh` now has a POSIX suite (`tools/__test__/install-sh.test.ts`) covering the piped invocation, the
34
+ checkout invocation, and a guard against reading `BASH_SOURCE` without a default. It skips on `win32`, so the
35
+ original decision is unchanged where it was actually about hardware: `install.ps1` and hooks firing inside an
36
+ editor session remain uncovered, because the operator still cannot prove them.
37
+
38
+ The distinction the first wording missed: *the shell installer is testable anywhere bash exists*, and only
39
+ the PowerShell installer and the editor end depend on Windows.
40
+
41
+ ## Applies to
42
+
43
+ - `src/platform/`
44
+ - installers
45
+ - README
46
+
47
+ ## See also
48
+
49
+ - `README.md` (repo root, outside this bundle) — states this caveat to users directly
@@ -0,0 +1,36 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-007 — Vendor check applies to core tests; absence checks do not"
4
+ description: "tools/check-boundaries.ts scans src/core/**/__test__/ for vendor identifiers but excludes __test__ from the HOME/.cursor absence checks, so core tests must use opaque provider names."
5
+ tags: [decision, boundaries, testing, core]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-007 — Vendor check applies to core tests; absence checks do not
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ `tools/check-boundaries.ts` scans `src/core/**/__test__/` for vendor identifiers, but excludes `__test__`
17
+ from the `process.env.HOME` and `.cursor/…` absence checks. Consequence: **core tests must use opaque
18
+ provider names** (`provider-a`, `provider-b`), never `cursor`/`claude`.
19
+
20
+ ## Why
21
+
22
+ A test asserting a string is absent must contain that string as the literal it searches for — so excluding
23
+ tests is correct for absence checks. But a core test that *needs* the literal `cursor` or `claude` is itself
24
+ the smell the vendor rule exists to catch; writing core tests with opaque names is what proves core treats
25
+ a provider name as an opaque string. Excluding tests there would have made the strongest guarantee opt-in.
26
+
27
+ ## Applies to
28
+
29
+ - `tools/check-boundaries.ts`
30
+ - every `src/core/**/__test__/` file
31
+ - design guarantees
32
+
33
+ ## See also
34
+
35
+ - [/decisions/ad-004.md](/decisions/ad-004.md) — the boundary this check enforces
36
+ - [/decisions/ad-011.md](/decisions/ad-011.md) — real vendor literals the check caught
@@ -0,0 +1,54 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-008 — Biome + TypeScript in the gate; @types/node pinned to the declared floor"
4
+ description: "devDependencies add @biomejs/biome@2.5.6 and typescript@7.0.2; the gate becomes biome check && tsc --noEmit && node --test && check-boundaries, and @types/node is pinned to ^24, not the latest 26.x."
5
+ tags: [decision, gate, typescript, biome]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-008 — Biome + TypeScript in the gate; `@types/node` pinned to the declared floor
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ devDependencies `@biomejs/biome@2.5.6` (lint + format) and `typescript@7.0.2` (`tsc --noEmit`). Gate becomes
17
+ `biome check && tsc --noEmit && node --test && check-boundaries`. `@types/node` pinned to `^24`, **not** the
18
+ latest 26.x. Both configs are scoped to the new module trees; a later widening task covers the rest of the
19
+ tree.
20
+
21
+ ## Why
22
+
23
+ The repo had no linter, no formatter, and no typechecker — `node --test`, esbuild, and Bun all strip types
24
+ without validating, so a "no TypeScript errors" criterion was unverifiable. This matters most where
25
+ `Decision` / `HarnessEvent` / `ProviderCapabilities` are discriminated unions that deliver nothing without
26
+ checking. Biome does **not** substitute for `tsc`: its type engine is deliberately independent of the
27
+ compiler and rule-scoped, which is fine for a lint rule and not for a build gate. `@types/node` is a
28
+ compatibility contract, not a tool — typing against Node 26 while `engines` declares `>=24` would let
29
+ Node-26-only APIs pass a gate that promises Node 24 support.
30
+
31
+ `noUncheckedIndexedAccess` stays on. It produces most of the diagnostic noise but catches exactly the class
32
+ of bug that appears when parsing vendor payloads (`match[1]`, `args[0]`, `parts[2]`) — the highest-value
33
+ flag for an adapter layer.
34
+
35
+ ## Applies to
36
+
37
+ - `tsconfig.json`
38
+ - `biome.json`
39
+ - `package.json`
40
+ - `CONTRIBUTING.md`
41
+ - every task's gate
42
+
43
+ ## Historical note — local Node below the declared floor
44
+
45
+ Earlier in the port, local `node` was **v22.22.3** while `engines.node` and the launcher required **24+**
46
+ (the launcher exits fatally below it), and `npm install` confirmed with `EBADENGINE`. Tests ran fine (type
47
+ stripping is unflagged since Node 22.18), but the harness itself could not run end-to-end locally until Node
48
+ was upgraded to 24 or 26. This was never a build blocker — only a blocker for a local end-to-end smoke of
49
+ the installed harness — and has since been resolved by upgrading the local Node runtime.
50
+
51
+ ## See also
52
+
53
+ - [/decisions/ad-005.md](/decisions/ad-005.md) — the gate this decision defines the contents of
54
+ - `CONTRIBUTING.md` (repo root, outside this bundle) — the checks a contributor runs
@@ -0,0 +1,61 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-009 — Event kinds are provider-agnostic; capabilities are data, not flags"
4
+ description: "The event-kind union covers what any provider emits, askSupportedOn is a list of kinds rather than a pair of booleans, and EffortLevel has five levels."
5
+ tags: [decision, design, cursor, contracts]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-009 — Event kinds are provider-agnostic; capabilities are data, not flags
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ Seven corrections to the event-kind union and the capability descriptor, each one settling how provider
17
+ asymmetry is represented.
18
+
19
+ 1. **18 event kinds, not 16.** Added `mcp.after` and `thought.after`. Both are wired to observability
20
+ already — returning `null` for them would silently drop signal that already exists. A kind exists when
21
+ *any* provider produces it; a provider that cannot is gated by its descriptor, not by the kind's absence.
22
+ 2. **`askAtToolLevel` + `askAtShellLevel` → `askSupportedOn: HarnessEventKind[]`.** A boolean pair cannot
23
+ express Cursor's real behavior (`ask` on `beforeShellExecution` and `beforeMCPExecution`, but not
24
+ `preToolUse`). Data instead of flags; a hookless provider gets `[]`.
25
+ 3. **`contextAtToolTime` → `contextAtToolBefore` + `contextAtToolAfter`.** The table listed `partial` for
26
+ Cursor — a non-boolean in a boolean field. The split is what `partial` meant: `postToolUse` carries
27
+ `additional_context`, `preToolUse` does not.
28
+ 4. **`HarnessEvent.text?: string` added.** `response.after` ship-claim detection needs the response body,
29
+ and `raw` is adapter-only by contract. Also serves `prompt.submit` and `thought.after`.
30
+ 5. **`EffortLevel` is five levels** — `low | medium | high | xhigh | max`, per Claude Code's documented
31
+ `effort.level`. A three-value type makes `isEffortLevel` reject `xhigh`/`max`, so `minEffort` would skip
32
+ silently on exactly the high-effort sessions it governs.
33
+ 6. **`continue` under `enforcesHooks: false` keeps the `ADVISORY —` prefix.** Unenforced guidance must
34
+ announce itself.
35
+ 7. **Provider registration is part of each provider's final task.** `providers/<name>/index.ts` composing
36
+ the pieces + one line in `provider.registry.ts`.
37
+
38
+ ## Why
39
+
40
+ The tables were written from vendor docs without being executed. Building Cursor against them — with
41
+ goldens captured from the real pre-refactor handlers — surfaced what documentation review could not. Items
42
+ 1, 3, and 5 are outright defects that would have shipped as silent signal loss; the rest are precision.
43
+
44
+ ## Applies to
45
+
46
+ - `src/providers/provider.types.ts` / `src/contracts/`
47
+ - `provider.degrade.ts`
48
+ - `cursor.capabilities.ts`
49
+ - the Claude adapter
50
+ - `minEffort` / ship-claim detection
51
+
52
+ ## Note — `ask` at `tool.before` is provider-specific
53
+
54
+ Cursor's `askSupportedOn` excludes `tool.before`, so its golden asserts `ask` never reaches the renderer
55
+ there. Claude's **includes** it (see [/providers/claude-code.md](/providers/claude-code.md)). That invariant
56
+ is Cursor's, not general — do not generalize it from reading only Cursor's tests.
57
+
58
+ ## See also
59
+
60
+ - [/providers/cursor.md](/providers/cursor.md), [/providers/claude-code.md](/providers/claude-code.md)
61
+ - [/decisions/ad-010.md](/decisions/ad-010.md) — where these contract types now live
@@ -0,0 +1,45 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-010 — Shared vocabulary moves to src/contracts/"
4
+ description: "HarnessEvent, Decision, Rendered, ProviderCapabilities, EffortLevel, and ProviderWiring live in a new src/contracts/ module holding types and pure helpers only; core and providers both import from it, neither imports the other."
5
+ tags: [decision, architecture, contracts]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-010 — Shared vocabulary moves to `src/contracts/`
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ `HarnessEvent`, `HarnessEventKind`, `Decision`, `Rendered`, `ProviderCapabilities`, `EffortLevel`,
17
+ `ProviderWiring` move into a **`src/contracts/`** module holding types and pure helpers only — zero
18
+ behavior, zero IO. Both `core/` and `providers/` import from it; neither imports the other.
19
+ `check-boundaries` forbids `core ↔ providers` and additionally forbids
20
+ `contracts → {core, providers, platform, entrypoints}`.
21
+
22
+ ## Why
23
+
24
+ The design's dependency diagram said core never imports providers, and separately that core returns
25
+ `Decision` values and that degradation consumes `HarnessEvent`. Those two statements are only compatible if
26
+ the vocabulary lives somewhere both can reach. It never said where.
27
+
28
+ ## Why a third module rather than putting the types in `core/`
29
+
30
+ Classic hexagonal would place port types in the domain and have adapters depend inward, which also works. A
31
+ separate contracts module was chosen because it keeps the boundary rule symmetric and mechanically
32
+ checkable (neither side may import the other, full stop), makes the shared surface explicit and small
33
+ enough to review, and makes behavior coupling impossible rather than merely discouraged — an adapter cannot
34
+ reach a core service even by accident.
35
+
36
+ ## Applies to
37
+
38
+ - `src/contracts/` (new)
39
+ - `src/providers/**` (imports)
40
+ - `tools/check-boundaries.ts`
41
+
42
+ ## See also
43
+
44
+ - [/architecture.md](/architecture.md) — how contracts sits between core and providers
45
+ - [/decisions/ad-004.md](/decisions/ad-004.md) — the ports-and-adapters shape this vocabulary serves
@@ -0,0 +1,59 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-011 — Vendor-specific data belongs to the provider, not to core"
4
+ description: "Model catalogs, cost pool names, and lessons rendering all move out of core and into each provider — four corrections surfaced by the vendor-identifier check hitting real code."
5
+ tags: [decision, providers, core, policy, pricing]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-011 — Vendor-specific data belongs to the provider, not to core
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ Four corrections, all surfaced by the vendor check hitting real code.
17
+
18
+ 1. **Model catalogs move to the provider.** `ProviderPort` gains
19
+ `policyDefaults(): { allowedModels, blockedPatterns, minEffort }`. Cursor supplies its own model list and
20
+ `-fast` patterns; Claude supplies its own. `core/policy` keeps only the *shape* and provider-neutral
21
+ patterns (`-fast`, `/fast`). `core/subagent-policy` receives the effective list as an argument, exactly
22
+ as it already receives `ProviderCapabilities`. Resolution at the entrypoint: project/user config →
23
+ provider defaults → core generic defaults.
24
+ 2. **Cost pool names in core become neutral**: `provider_native | other | auto | unknown`. Vendor-named
25
+ pools (e.g. a Cursor-specific catalog key) move to `platform/pricing.ts`, which is correctly outside the
26
+ vendor scan because pricing must name real vendors. `cost_source` is `"provider"`, not a vendor name.
27
+ 3. **`FailureCategory` and `GateGap` get one home** — `core/gate/gate.types.ts`. `core/handoff` and
28
+ `core/lesson` import the types from there instead of each hand-copying them. Sibling type imports inside
29
+ `core/` are fine; only cross-*module* imports are barred.
30
+ 4. **Provider-side lessons rendering is assigned.** `.tlc/harness/lessons.md` is the source of truth.
31
+ Rendering the provider-native view is each provider's own concern: Cursor's
32
+ `.cursor/rules/harness-lessons.mdc` and Claude's `@.tlc/harness/lessons.md` line in `CLAUDE.md` each
33
+ belong to that provider's wiring.
34
+
35
+ ## Why
36
+
37
+ The vendor check did its job — it caught that a Cursor model catalog was sitting in what is supposed to be
38
+ provider-agnostic core. Two vendor literals were also passing only because `_` suppresses the `\b` word
39
+ boundary in the scan regex, i.e. tolerated by a regex accident rather than by design. Fixing them properly
40
+ puts vendor knowledge where the anti-corruption layer says it goes.
41
+
42
+ ## Note
43
+
44
+ An early interim shipped `allowedModels: []`, which is functionally inert while `enforceAllowlist` defaults
45
+ `false` but becomes a footgun the moment a user enables it. Item 1 removes that footgun.
46
+
47
+ ## Applies to
48
+
49
+ - `contracts/` (port shape)
50
+ - `providers/cursor/`, `providers/claude/`
51
+ - `core/policy`, `core/subagent-policy`
52
+ - `core/observability`, `core/gate`, `core/handoff`, `core/lesson`
53
+ - `platform/pricing.ts`
54
+
55
+ ## See also
56
+
57
+ - [/measure.md](/measure.md) — the neutral cost pool names in the observability record shape
58
+ - [/providers/index.md](/providers/index.md) — `policyDefaults()` on each provider
59
+ - [/decisions/ad-007.md](/decisions/ad-007.md) — the check that caught this
@@ -0,0 +1,71 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-012 — Prefer Bun at runtime, keep dist/ for the Node fallback, ship no binary"
4
+ description: "tlc-exec tries Bun first, falling back to Node + dist/*.mjs; dist/ stays tracked since the installer is a git clone with no build step, and no compiled single-file binary ships."
5
+ tags: [decision, runtime, bun, performance]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-012 — Prefer Bun at runtime, keep `dist/` for the Node fallback, ship no binary
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ `tlc-exec` tries **Bun first** when it is on PATH, falling back to Node + `dist/*.mjs`. `dist/` stays
17
+ tracked (the installer is `git clone` with no build step). No compiled single-file binary.
18
+
19
+ ## Measured
20
+
21
+ Same handler, 30 warm invocations each, on the development machine:
22
+
23
+ | Path | Per invocation |
24
+ | --- | --- |
25
+ | `bun run src/*.ts` | ~1 ms |
26
+ | `bun run dist/*.mjs` | ~1 ms |
27
+ | `node dist/*.mjs` | 27 ms |
28
+ | `node src/*.ts` | 63 ms |
29
+
30
+ ## Why
31
+
32
+ Hooks are inner-loop — they fire on every tool call, shell command, read, and stop. At ~30 fires per agent
33
+ turn that is ~810 ms of pure process startup on Node versus ~30 ms on Bun. Output is byte-identical across
34
+ runtimes — verified on `guard-shell` (`{"permission":"allow"}`).
35
+
36
+ `dist/` still earns its keep only for the Node path, where it is 2.3× faster than stripping types at
37
+ runtime (27 ms vs 63 ms). Under Bun it buys nothing, which is why it is a fallback artifact rather than the
38
+ primary one.
39
+
40
+ No compiled binary because a Bun-compiled executable is 50–100 MB per platform and this repo both installs
41
+ by `git clone` and tracks its build output — four platform binaries in git history is untenable. The gain
42
+ over `bun run` is also ~zero at this scale; the published latency case for binaries is against a *cold Node*
43
+ baseline, which the Bun-first path already removes.
44
+
45
+ Using the harness never requires Bun — Bun is the fast path, Node stays the guaranteed path.
46
+
47
+ **Surface the fallback, never fall back silently.** A user on the 27× slower path must know they are on it
48
+ and how to leave it. `tlc harness doctor` and `tlc harness status` both report the resolved runtime with the
49
+ measured cost of the gap and the one-line fix (install Bun). Absent Bun is a `WARN`, not a `FAIL` — Node is
50
+ supported, just slower.
51
+
52
+ **Detection must not cost a spawn per hook.** `spawnSync(bun, ["--version"])` on every invocation would burn
53
+ more than the 26 ms it saves. Resolve by testing for the executable on `PATH` without executing it, and
54
+ cache the result in `state/` keyed by the resolved path; refresh on `doctor` / `status` / `update`.
55
+
56
+ ## Context
57
+
58
+ Claude Code itself is TypeScript on Bun, shipped as a Bun-compiled binary; other CLI agent tools are
59
+ TS/Node or have migrated to a compiled systems language. TS-on-Bun is the incumbent pattern for this exact
60
+ problem domain.
61
+
62
+ ## Applies to
63
+
64
+ - `bin/tlc-exec.mjs`
65
+ - `bin/tlc-build`
66
+ - [/architecture.md](/architecture.md)
67
+
68
+ ## See also
69
+
70
+ - [/measure.md](/measure.md) — how `doctor`/`status` surface the resolved runtime
71
+ - [/init.md](/init.md) — Step 1b of the init skill, which presents this trade-off to the user
@@ -0,0 +1,87 @@
1
+ ---
2
+ type: Decision
3
+ title: "AD-013 — Documentation follows the Open Knowledge Format (OKF v0.1)"
4
+ description: "docs/ becomes an OKF v0.1 bundle — markdown with YAML frontmatter, index.md and log.md as reserved files, absolute bundle-relative cross-links, and a closed type vocabulary."
5
+ tags: [decision, docs, okf]
6
+ timestamp: "2026-07-29"
7
+ ---
8
+
9
+ # AD-013 — Documentation follows the Open Knowledge Format (OKF v0.1)
10
+
11
+ - **status**: active
12
+ - **date**: 2026-07-29
13
+
14
+ ## Decision
15
+
16
+ `docs/` becomes an OKF v0.1 bundle — markdown with YAML frontmatter, `index.md` and `log.md` as reserved
17
+ files, absolute bundle-relative cross-links.
18
+
19
+ ## Layout
20
+
21
+ ```
22
+ docs/
23
+ ├── index.md okf_version: "0.1", entries from concept descriptions
24
+ ├── log.md chronological, ISO 8601
25
+ ├── architecture.md type: Concept
26
+ ├── concepts.md type: Concept
27
+ ├── diagnose.md type: Runbook
28
+ ├── init.md type: Runbook
29
+ ├── lessons.md type: Concept
30
+ ├── measure.md type: Runbook
31
+ ├── providers/{index,cursor,claude-code}.md type: Aggregate / Provider
32
+ └── decisions/{index,ad-001…ad-016}.md type: Aggregate / Decision
33
+ ```
34
+
35
+ ## `type` vocabulary
36
+
37
+ Closed, small: `Concept`, `Runbook`, `Provider`, `Decision`, `Capability`, `Aggregate`. OKF has no central
38
+ registry, so the constraint is ours to keep.
39
+
40
+ ## Frontmatter
41
+
42
+ `type` is the only field the spec requires. This bundle also always sets `title`, `description`, `tags`,
43
+ `timestamp`. Unknown keys must be preserved on round-trip; broken links are valid and mean unrealized
44
+ knowledge.
45
+
46
+ ## Links
47
+
48
+ Absolute (`/decisions/ad-010.md`), not relative — they survive a document moving between subdirectories.
49
+
50
+ ## Citations
51
+
52
+ Any doc asserting something from an external source carries a numbered `# Citations` section, per the spec.
53
+
54
+ ## Why
55
+
56
+ This repo is a harness *for coding agents*; its own documentation should be machine-consumable by the
57
+ agents it steers. OKF is markdown with structured metadata, needs no SDK, no registry and no service, and
58
+ the spec explicitly targets keeping knowledge beside the code so an agent reads both from one clone.
59
+
60
+ ## Why this also splits `STATE.md`
61
+
62
+ `AD-001`…`AD-016` were sixteen-plus sections in one growing file. As OKF concepts — one decision per file,
63
+ `type: Decision`, cross-linked — they become addressable: an agent asking "why does `contracts/` exist"
64
+ resolves `/decisions/ad-010.md` directly instead of scanning one long document.
65
+
66
+ ## Note on the sibling repo
67
+
68
+ Some projects use OKF for **runtime knowledge** (an agent's own `knowledge/` namespace), not for repo
69
+ docs — its `docs/*.md` carry no frontmatter. Applying it to docs here is a different and additional use, not
70
+ a copy of that setup.
71
+
72
+ ## Applies to
73
+
74
+ - `docs/**`
75
+ - `.specs/STATE.md` (split)
76
+ - `tools/check-docs-bundle.ts`
77
+
78
+ ## See also
79
+
80
+ - [/index.md](/index.md), [/log.md](/log.md)
81
+ - [/decisions/index.md](/decisions/index.md)
82
+
83
+ # Citations
84
+
85
+ 1. Open Knowledge Format annotated specification — https://okf.md/spec/
86
+ 2. GitBook, *What is OKF? Understanding Google's Open Knowledge Format* — https://www.gitbook.com/blog/what-is-okf-open-knowledge-format
87
+ 3. Reference implementation, `GoogleCloudPlatform/knowledge-catalog` (Apache 2.0)