@ai-substrate/engineering-harness 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -0
  3. package/harness/cli/bin/harness.js +12 -0
  4. package/harness/cli/dist/acts/docs.d.ts +17 -0
  5. package/harness/cli/dist/acts/docs.js +73 -0
  6. package/harness/cli/dist/acts/docs.js.map +1 -0
  7. package/harness/cli/dist/acts/doctor.d.ts +14 -0
  8. package/harness/cli/dist/acts/doctor.js +43 -0
  9. package/harness/cli/dist/acts/doctor.js.map +1 -0
  10. package/harness/cli/dist/acts/help.d.ts +14 -0
  11. package/harness/cli/dist/acts/help.js +29 -0
  12. package/harness/cli/dist/acts/help.js.map +1 -0
  13. package/harness/cli/dist/acts/init.d.ts +22 -0
  14. package/harness/cli/dist/acts/init.js +61 -0
  15. package/harness/cli/dist/acts/init.js.map +1 -0
  16. package/harness/cli/dist/acts/instructions.d.ts +21 -0
  17. package/harness/cli/dist/acts/instructions.js +75 -0
  18. package/harness/cli/dist/acts/instructions.js.map +1 -0
  19. package/harness/cli/dist/acts/new.d.ts +19 -0
  20. package/harness/cli/dist/acts/new.js +66 -0
  21. package/harness/cli/dist/acts/new.js.map +1 -0
  22. package/harness/cli/dist/acts/observe.d.ts +23 -0
  23. package/harness/cli/dist/acts/observe.js +129 -0
  24. package/harness/cli/dist/acts/observe.js.map +1 -0
  25. package/harness/cli/dist/acts/record.d.ts +24 -0
  26. package/harness/cli/dist/acts/record.js +93 -0
  27. package/harness/cli/dist/acts/record.js.map +1 -0
  28. package/harness/cli/dist/acts/skills.d.ts +32 -0
  29. package/harness/cli/dist/acts/skills.js +256 -0
  30. package/harness/cli/dist/acts/skills.js.map +1 -0
  31. package/harness/cli/dist/acts/update.d.ts +23 -0
  32. package/harness/cli/dist/acts/update.js +297 -0
  33. package/harness/cli/dist/acts/update.js.map +1 -0
  34. package/harness/cli/dist/acts/verb.d.ts +27 -0
  35. package/harness/cli/dist/acts/verb.js +56 -0
  36. package/harness/cli/dist/acts/verb.js.map +1 -0
  37. package/harness/cli/dist/adapters/clock/clock-port.d.ts +10 -0
  38. package/harness/cli/dist/adapters/clock/clock-port.js +2 -0
  39. package/harness/cli/dist/adapters/clock/clock-port.js.map +1 -0
  40. package/harness/cli/dist/adapters/clock/fake-clock.d.ts +15 -0
  41. package/harness/cli/dist/adapters/clock/fake-clock.js +25 -0
  42. package/harness/cli/dist/adapters/clock/fake-clock.js.map +1 -0
  43. package/harness/cli/dist/adapters/clock/system-clock.d.ts +5 -0
  44. package/harness/cli/dist/adapters/clock/system-clock.js +7 -0
  45. package/harness/cli/dist/adapters/clock/system-clock.js.map +1 -0
  46. package/harness/cli/dist/adapters/env/env-port.d.ts +17 -0
  47. package/harness/cli/dist/adapters/env/env-port.js +2 -0
  48. package/harness/cli/dist/adapters/env/env-port.js.map +1 -0
  49. package/harness/cli/dist/adapters/env/fake-env.d.ts +15 -0
  50. package/harness/cli/dist/adapters/env/fake-env.js +24 -0
  51. package/harness/cli/dist/adapters/env/fake-env.js.map +1 -0
  52. package/harness/cli/dist/adapters/env/node-env.d.ts +6 -0
  53. package/harness/cli/dist/adapters/env/node-env.js +16 -0
  54. package/harness/cli/dist/adapters/env/node-env.js.map +1 -0
  55. package/harness/cli/dist/adapters/exec/exec-port.d.ts +22 -0
  56. package/harness/cli/dist/adapters/exec/exec-port.js +2 -0
  57. package/harness/cli/dist/adapters/exec/exec-port.js.map +1 -0
  58. package/harness/cli/dist/adapters/exec/fake-exec.d.ts +25 -0
  59. package/harness/cli/dist/adapters/exec/fake-exec.js +25 -0
  60. package/harness/cli/dist/adapters/exec/fake-exec.js.map +1 -0
  61. package/harness/cli/dist/adapters/exec/node-exec.d.ts +14 -0
  62. package/harness/cli/dist/adapters/exec/node-exec.js +38 -0
  63. package/harness/cli/dist/adapters/exec/node-exec.js.map +1 -0
  64. package/harness/cli/dist/adapters/fs/fake-fs.d.ts +22 -0
  65. package/harness/cli/dist/adapters/fs/fake-fs.js +63 -0
  66. package/harness/cli/dist/adapters/fs/fake-fs.js.map +1 -0
  67. package/harness/cli/dist/adapters/fs/fs-port.d.ts +20 -0
  68. package/harness/cli/dist/adapters/fs/fs-port.js +2 -0
  69. package/harness/cli/dist/adapters/fs/fs-port.js.map +1 -0
  70. package/harness/cli/dist/adapters/fs/node-fs.d.ts +9 -0
  71. package/harness/cli/dist/adapters/fs/node-fs.js +30 -0
  72. package/harness/cli/dist/adapters/fs/node-fs.js.map +1 -0
  73. package/harness/cli/dist/adapters/git/exec-git.d.ts +6 -0
  74. package/harness/cli/dist/adapters/git/exec-git.js +21 -0
  75. package/harness/cli/dist/adapters/git/exec-git.js.map +1 -0
  76. package/harness/cli/dist/adapters/git/fake-git.d.ts +15 -0
  77. package/harness/cli/dist/adapters/git/fake-git.js +20 -0
  78. package/harness/cli/dist/adapters/git/fake-git.js.map +1 -0
  79. package/harness/cli/dist/adapters/git/git-port.d.ts +12 -0
  80. package/harness/cli/dist/adapters/git/git-port.js +2 -0
  81. package/harness/cli/dist/adapters/git/git-port.js.map +1 -0
  82. package/harness/cli/dist/adapters/loader/fake-loader.d.ts +13 -0
  83. package/harness/cli/dist/adapters/loader/fake-loader.js +25 -0
  84. package/harness/cli/dist/adapters/loader/fake-loader.js.map +1 -0
  85. package/harness/cli/dist/adapters/loader/jiti-loader.d.ts +15 -0
  86. package/harness/cli/dist/adapters/loader/jiti-loader.js +29 -0
  87. package/harness/cli/dist/adapters/loader/jiti-loader.js.map +1 -0
  88. package/harness/cli/dist/adapters/loader/module-loader-port.d.ts +12 -0
  89. package/harness/cli/dist/adapters/loader/module-loader-port.js +2 -0
  90. package/harness/cli/dist/adapters/loader/module-loader-port.js.map +1 -0
  91. package/harness/cli/dist/adapters/process/fake-process.d.ts +13 -0
  92. package/harness/cli/dist/adapters/process/fake-process.js +21 -0
  93. package/harness/cli/dist/adapters/process/fake-process.js.map +1 -0
  94. package/harness/cli/dist/adapters/process/node-process.d.ts +6 -0
  95. package/harness/cli/dist/adapters/process/node-process.js +17 -0
  96. package/harness/cli/dist/adapters/process/node-process.js.map +1 -0
  97. package/harness/cli/dist/adapters/process/process-port.d.ts +13 -0
  98. package/harness/cli/dist/adapters/process/process-port.js +2 -0
  99. package/harness/cli/dist/adapters/process/process-port.js.map +1 -0
  100. package/harness/cli/dist/adapters/version-lookup/fake-version-lookup.d.ts +13 -0
  101. package/harness/cli/dist/adapters/version-lookup/fake-version-lookup.js +21 -0
  102. package/harness/cli/dist/adapters/version-lookup/fake-version-lookup.js.map +1 -0
  103. package/harness/cli/dist/adapters/version-lookup/node-version-lookup.d.ts +18 -0
  104. package/harness/cli/dist/adapters/version-lookup/node-version-lookup.js +51 -0
  105. package/harness/cli/dist/adapters/version-lookup/node-version-lookup.js.map +1 -0
  106. package/harness/cli/dist/adapters/version-lookup/version-lookup-port.d.ts +19 -0
  107. package/harness/cli/dist/adapters/version-lookup/version-lookup-port.js +2 -0
  108. package/harness/cli/dist/adapters/version-lookup/version-lookup-port.js.map +1 -0
  109. package/harness/cli/dist/app.d.ts +70 -0
  110. package/harness/cli/dist/app.js +221 -0
  111. package/harness/cli/dist/app.js.map +1 -0
  112. package/harness/cli/dist/index.d.ts +2 -0
  113. package/harness/cli/dist/index.js +26 -0
  114. package/harness/cli/dist/index.js.map +1 -0
  115. package/harness/cli/dist/output/envelope.d.ts +68 -0
  116. package/harness/cli/dist/output/envelope.js +56 -0
  117. package/harness/cli/dist/output/envelope.js.map +1 -0
  118. package/harness/cli/dist/output/error-codes.d.ts +57 -0
  119. package/harness/cli/dist/output/error-codes.js +57 -0
  120. package/harness/cli/dist/output/error-codes.js.map +1 -0
  121. package/harness/cli/dist/output/exit.d.ts +29 -0
  122. package/harness/cli/dist/output/exit.js +36 -0
  123. package/harness/cli/dist/output/exit.js.map +1 -0
  124. package/harness/cli/dist/output/output-port.d.ts +54 -0
  125. package/harness/cli/dist/output/output-port.js +55 -0
  126. package/harness/cli/dist/output/output-port.js.map +1 -0
  127. package/harness/cli/dist/output/style.d.ts +33 -0
  128. package/harness/cli/dist/output/style.js +68 -0
  129. package/harness/cli/dist/output/style.js.map +1 -0
  130. package/harness/cli/dist/services/config/load-config.d.ts +27 -0
  131. package/harness/cli/dist/services/config/load-config.js +114 -0
  132. package/harness/cli/dist/services/config/load-config.js.map +1 -0
  133. package/harness/cli/dist/services/docs/contract.d.ts +41 -0
  134. package/harness/cli/dist/services/docs/contract.js +14 -0
  135. package/harness/cli/dist/services/docs/contract.js.map +1 -0
  136. package/harness/cli/dist/services/docs/docs-content.d.ts +37 -0
  137. package/harness/cli/dist/services/docs/docs-content.js +48 -0
  138. package/harness/cli/dist/services/docs/docs-content.js.map +1 -0
  139. package/harness/cli/dist/services/docs/docs-service.d.ts +26 -0
  140. package/harness/cli/dist/services/docs/docs-service.js +25 -0
  141. package/harness/cli/dist/services/docs/docs-service.js.map +1 -0
  142. package/harness/cli/dist/services/doctor/doctor-service.d.ts +69 -0
  143. package/harness/cli/dist/services/doctor/doctor-service.js +237 -0
  144. package/harness/cli/dist/services/doctor/doctor-service.js.map +1 -0
  145. package/harness/cli/dist/services/extensions/contract.d.ts +138 -0
  146. package/harness/cli/dist/services/extensions/contract.js +17 -0
  147. package/harness/cli/dist/services/extensions/contract.js.map +1 -0
  148. package/harness/cli/dist/services/extensions/discovery.d.ts +53 -0
  149. package/harness/cli/dist/services/extensions/discovery.js +116 -0
  150. package/harness/cli/dist/services/extensions/discovery.js.map +1 -0
  151. package/harness/cli/dist/services/extensions/registry.d.ts +63 -0
  152. package/harness/cli/dist/services/extensions/registry.js +165 -0
  153. package/harness/cli/dist/services/extensions/registry.js.map +1 -0
  154. package/harness/cli/dist/services/extensions/verb-context.d.ts +44 -0
  155. package/harness/cli/dist/services/extensions/verb-context.js +97 -0
  156. package/harness/cli/dist/services/extensions/verb-context.js.map +1 -0
  157. package/harness/cli/dist/services/help/help-service.d.ts +42 -0
  158. package/harness/cli/dist/services/help/help-service.js +108 -0
  159. package/harness/cli/dist/services/help/help-service.js.map +1 -0
  160. package/harness/cli/dist/services/init/governance-template.d.ts +27 -0
  161. package/harness/cli/dist/services/init/governance-template.js +72 -0
  162. package/harness/cli/dist/services/init/governance-template.js.map +1 -0
  163. package/harness/cli/dist/services/init/init-service.d.ts +38 -0
  164. package/harness/cli/dist/services/init/init-service.js +44 -0
  165. package/harness/cli/dist/services/init/init-service.js.map +1 -0
  166. package/harness/cli/dist/services/instructions/core-instructions.d.ts +11 -0
  167. package/harness/cli/dist/services/instructions/core-instructions.js +80 -0
  168. package/harness/cli/dist/services/instructions/core-instructions.js.map +1 -0
  169. package/harness/cli/dist/services/instructions/instructions-service.d.ts +52 -0
  170. package/harness/cli/dist/services/instructions/instructions-service.js +53 -0
  171. package/harness/cli/dist/services/instructions/instructions-service.js.map +1 -0
  172. package/harness/cli/dist/services/observe/buffer-codec.d.ts +51 -0
  173. package/harness/cli/dist/services/observe/buffer-codec.js +139 -0
  174. package/harness/cli/dist/services/observe/buffer-codec.js.map +1 -0
  175. package/harness/cli/dist/services/observe/observe-service.d.ts +87 -0
  176. package/harness/cli/dist/services/observe/observe-service.js +221 -0
  177. package/harness/cli/dist/services/observe/observe-service.js.map +1 -0
  178. package/harness/cli/dist/services/record/contract.d.ts +32 -0
  179. package/harness/cli/dist/services/record/contract.js +17 -0
  180. package/harness/cli/dist/services/record/contract.js.map +1 -0
  181. package/harness/cli/dist/services/record/core-types/retro.d.ts +20 -0
  182. package/harness/cli/dist/services/record/core-types/retro.js +55 -0
  183. package/harness/cli/dist/services/record/core-types/retro.js.map +1 -0
  184. package/harness/cli/dist/services/record/record-service.d.ts +38 -0
  185. package/harness/cli/dist/services/record/record-service.js +144 -0
  186. package/harness/cli/dist/services/record/record-service.js.map +1 -0
  187. package/harness/cli/dist/services/record/registry.d.ts +46 -0
  188. package/harness/cli/dist/services/record/registry.js +71 -0
  189. package/harness/cli/dist/services/record/registry.js.map +1 -0
  190. package/harness/cli/dist/services/scaffold/scaffold-service.d.ts +29 -0
  191. package/harness/cli/dist/services/scaffold/scaffold-service.js +88 -0
  192. package/harness/cli/dist/services/scaffold/scaffold-service.js.map +1 -0
  193. package/harness/cli/dist/services/scaffold/templates.d.ts +42 -0
  194. package/harness/cli/dist/services/scaffold/templates.js +178 -0
  195. package/harness/cli/dist/services/scaffold/templates.js.map +1 -0
  196. package/harness/cli/dist/services/shared/posix-path.d.ts +54 -0
  197. package/harness/cli/dist/services/shared/posix-path.js +94 -0
  198. package/harness/cli/dist/services/shared/posix-path.js.map +1 -0
  199. package/harness/cli/dist/services/shared/temp.d.ts +24 -0
  200. package/harness/cli/dist/services/shared/temp.js +29 -0
  201. package/harness/cli/dist/services/shared/temp.js.map +1 -0
  202. package/harness/cli/dist/services/skills/contract.d.ts +52 -0
  203. package/harness/cli/dist/services/skills/contract.js +55 -0
  204. package/harness/cli/dist/services/skills/contract.js.map +1 -0
  205. package/harness/cli/dist/services/skills/skills-service.d.ts +73 -0
  206. package/harness/cli/dist/services/skills/skills-service.js +132 -0
  207. package/harness/cli/dist/services/skills/skills-service.js.map +1 -0
  208. package/harness/cli/dist/services/update/banner.d.ts +26 -0
  209. package/harness/cli/dist/services/update/banner.js +28 -0
  210. package/harness/cli/dist/services/update/banner.js.map +1 -0
  211. package/harness/cli/dist/services/update/cache.d.ts +21 -0
  212. package/harness/cli/dist/services/update/cache.js +61 -0
  213. package/harness/cli/dist/services/update/cache.js.map +1 -0
  214. package/harness/cli/dist/services/update/constants.d.ts +9 -0
  215. package/harness/cli/dist/services/update/constants.js +10 -0
  216. package/harness/cli/dist/services/update/constants.js.map +1 -0
  217. package/harness/cli/dist/services/update/install.d.ts +26 -0
  218. package/harness/cli/dist/services/update/install.js +78 -0
  219. package/harness/cli/dist/services/update/install.js.map +1 -0
  220. package/harness/cli/dist/services/update/semver.d.ts +16 -0
  221. package/harness/cli/dist/services/update/semver.js +108 -0
  222. package/harness/cli/dist/services/update/semver.js.map +1 -0
  223. package/harness/cli/dist/services/update/update-service.d.ts +46 -0
  224. package/harness/cli/dist/services/update/update-service.js +91 -0
  225. package/harness/cli/dist/services/update/update-service.js.map +1 -0
  226. package/harness/cli/dist/version.d.ts +8 -0
  227. package/harness/cli/dist/version.js +15 -0
  228. package/harness/cli/dist/version.js.map +1 -0
  229. package/package.json +56 -0
@@ -0,0 +1,53 @@
1
+ import { posixDirname, posixJoin } from '../shared/posix-path.js';
2
+ import { CORE_INSTRUCTIONS } from './core-instructions.js';
3
+ /**
4
+ * Verb → briefing path (plan 014 D5): find the record whose accepted `verbs[]`
5
+ * contains the verb, take `dirname(entryPath)`, join `instructions.md`. The
6
+ * contract stays untouched — no `instructions` field anywhere — and multi-verb
7
+ * extensions share their folder's single file for free. `null` = unknown verb.
8
+ */
9
+ export function instructionsPathFor(verbName, registry) {
10
+ const record = registry.records.find((r) => r.verbs.some((v) => v.name === verbName));
11
+ if (record === undefined) {
12
+ return null;
13
+ }
14
+ // entryPath is POSIX from discovery (plan 017) — derive in POSIX space so the
15
+ // surfaced briefing path stays forward-slash on every OS.
16
+ return posixJoin(posixDirname(record.entryPath), 'instructions.md');
17
+ }
18
+ /**
19
+ * Build the bare-invocation payload. `verbs_with_instructions` is an `FsPort`
20
+ * existence probe per registered verb at build time (D4) — cheap, and honest
21
+ * about what is queryable right now.
22
+ */
23
+ export function buildCoreInstructions(registry, fs) {
24
+ const verbs_with_instructions = registry.verbs
25
+ .filter((verb) => {
26
+ const path = instructionsPathFor(verb.name, registry);
27
+ return path !== null && fs.exists(path);
28
+ })
29
+ .map((verb) => verb.name);
30
+ return { instructions: CORE_INSTRUCTIONS, verbs_with_instructions };
31
+ }
32
+ /**
33
+ * Load a verb's briefing from disk NOW (D4 — lazily at invocation, never cached
34
+ * across calls, so an edit is visible on the next invocation with no rebuild).
35
+ * FsPort semantics make the missing/unreadable split: `exists()` false → the
36
+ * file was never authored (a gap); `exists()` true but `readText()` null → it
37
+ * is there but unreadable (a fault).
38
+ */
39
+ export function loadVerbInstructions(verbName, registry, fs) {
40
+ const path = instructionsPathFor(verbName, registry);
41
+ if (path === null) {
42
+ return { kind: 'unknown-verb', verb: verbName };
43
+ }
44
+ if (!fs.exists(path)) {
45
+ return { kind: 'missing', verb: verbName, path };
46
+ }
47
+ const instructions = fs.readText(path);
48
+ if (instructions === null) {
49
+ return { kind: 'unreadable', verb: verbName, path };
50
+ }
51
+ return { kind: 'ok', verb: verbName, path, instructions };
52
+ }
53
+ //# sourceMappingURL=instructions-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instructions-service.js","sourceRoot":"","sources":["../../../src/services/instructions/instructions-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAqB3D;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,QAAsB;IAC1E,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC;IACtF,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,8EAA8E;IAC9E,0DAA0D;IAC1D,OAAO,SAAS,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC,CAAC;AACtE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAsB,EAAE,EAAU;IACtE,MAAM,uBAAuB,GAAG,QAAQ,CAAC,KAAK;SAC3C,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;QACf,MAAM,IAAI,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACtD,OAAO,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC,CAAC;SACD,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,CAAC;AACtE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAgB,EAChB,QAAsB,EACtB,EAAU;IAEV,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACrD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACnD,CAAC;IACD,MAAM,YAAY,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACtD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * The entry grammar for the transient observation buffer (plan 015 D2): the
3
+ * exact YAML-blocks-in-markdown shape the old observe skill taught, kept so
4
+ * legacy hand-written buffers parse natively (D3). The CLI is the sole writer
5
+ * going forward — a constrained serializer (flat fields + the `system.compound`
6
+ * block, stable field order) paired with a tolerant line-based parser. No
7
+ * `yaml` runtime dependency: the grammar is a small contract and the
8
+ * npx-installed core stays at commander+jiti only (constitution P10).
9
+ */
10
+ /** The 7 universal-schema kinds and their ID prefixes (workshop 005 § D5/D6). */
11
+ export declare const OBSERVATION_KINDS: {
12
+ readonly difficulty: "DL";
13
+ readonly 'magic-wand': "MW";
14
+ readonly gift: "GFT";
15
+ readonly insight: "INS";
16
+ readonly coordination: "COORD";
17
+ readonly 'improvement-suggestion': "SUGG";
18
+ readonly confusion: "CONF";
19
+ };
20
+ export type ObservationKind = keyof typeof OBSERVATION_KINDS;
21
+ export declare const OBSERVATION_SEVERITIES: readonly ["blocking", "degrading", "annoying"];
22
+ export interface ObservationEntry {
23
+ id: string;
24
+ kind: string;
25
+ description: string;
26
+ target?: string;
27
+ severity?: string;
28
+ workaround?: string;
29
+ suggested_encoding?: string;
30
+ /** From the nested `system.compound` block. */
31
+ first_seen_at?: string;
32
+ }
33
+ /**
34
+ * Serialize one entry as an append-ready YAML block — identical in shape to the
35
+ * old skill's template, including the full `system.compound` lifecycle block
36
+ * (D10) so drained entries carry the fields the harvest already expects.
37
+ */
38
+ export declare function serializeEntry(entry: ObservationEntry): string;
39
+ /**
40
+ * Tolerant parse of a whole buffer: split on `- id:` block starts; a block that
41
+ * fails to parse or lacks a required field (valid id, known kind, non-empty
42
+ * description) counts as *malformed* — skipped, counted, and returned RAW in
43
+ * `deviant` so callers can preserve it on disk (D3: nothing is silently
44
+ * dropped, not even by `--clear`). Non-blank content before the first block
45
+ * counts as one malformed chunk.
46
+ */
47
+ export declare function parseBuffer(content: string): {
48
+ entries: ObservationEntry[];
49
+ malformed: number;
50
+ deviant: string[];
51
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The entry grammar for the transient observation buffer (plan 015 D2): the
3
+ * exact YAML-blocks-in-markdown shape the old observe skill taught, kept so
4
+ * legacy hand-written buffers parse natively (D3). The CLI is the sole writer
5
+ * going forward — a constrained serializer (flat fields + the `system.compound`
6
+ * block, stable field order) paired with a tolerant line-based parser. No
7
+ * `yaml` runtime dependency: the grammar is a small contract and the
8
+ * npx-installed core stays at commander+jiti only (constitution P10).
9
+ */
10
+ /** The 7 universal-schema kinds and their ID prefixes (workshop 005 § D5/D6). */
11
+ export const OBSERVATION_KINDS = {
12
+ difficulty: 'DL',
13
+ 'magic-wand': 'MW',
14
+ gift: 'GFT',
15
+ insight: 'INS',
16
+ coordination: 'COORD',
17
+ 'improvement-suggestion': 'SUGG',
18
+ confusion: 'CONF',
19
+ };
20
+ export const OBSERVATION_SEVERITIES = ['blocking', 'degrading', 'annoying'];
21
+ /** `<PREFIX>-<3+ digits>` per workshop 005 § D6 — anything else is a deviant block. */
22
+ const ID_PATTERN = /^[A-Z]+-\d{3,}$/;
23
+ /** The flat entry fields (plus first_seen_at from the nested block), any indent. */
24
+ const FIELD_PATTERN = /^\s+(kind|description|target|severity|workaround|suggested_encoding|first_seen_at):\s*(.*)$/;
25
+ /**
26
+ * Serialize one entry as an append-ready YAML block — identical in shape to the
27
+ * old skill's template, including the full `system.compound` lifecycle block
28
+ * (D10) so drained entries carry the fields the harvest already expects.
29
+ */
30
+ export function serializeEntry(entry) {
31
+ const lines = [
32
+ `- id: ${entry.id}`,
33
+ ` kind: ${entry.kind}`,
34
+ ` description: ${JSON.stringify(entry.description)}`,
35
+ ];
36
+ if (entry.target !== undefined)
37
+ lines.push(` target: ${entry.target}`);
38
+ if (entry.severity !== undefined)
39
+ lines.push(` severity: ${entry.severity}`);
40
+ if (entry.workaround !== undefined) {
41
+ lines.push(` workaround: ${JSON.stringify(entry.workaround)}`);
42
+ }
43
+ if (entry.suggested_encoding !== undefined) {
44
+ lines.push(` suggested_encoding: ${JSON.stringify(entry.suggested_encoding)}`);
45
+ }
46
+ lines.push(' system:', ' compound:', ' status: open', ' source: agent-self', ` first_seen_at: "${entry.first_seen_at ?? ''}"`);
47
+ return `${lines.join('\n')}\n`;
48
+ }
49
+ /**
50
+ * Tolerant parse of a whole buffer: split on `- id:` block starts; a block that
51
+ * fails to parse or lacks a required field (valid id, known kind, non-empty
52
+ * description) counts as *malformed* — skipped, counted, and returned RAW in
53
+ * `deviant` so callers can preserve it on disk (D3: nothing is silently
54
+ * dropped, not even by `--clear`). Non-blank content before the first block
55
+ * counts as one malformed chunk.
56
+ */
57
+ export function parseBuffer(content) {
58
+ const entries = [];
59
+ const deviant = [];
60
+ let malformed = 0;
61
+ if (content.trim().length === 0) {
62
+ return { entries, malformed, deviant };
63
+ }
64
+ const lines = content.split('\n');
65
+ const starts = [];
66
+ lines.forEach((line, i) => {
67
+ if (line.startsWith('- id:'))
68
+ starts.push(i);
69
+ });
70
+ const preamble = lines.slice(0, starts[0] ?? lines.length).join('\n');
71
+ if (preamble.trim().length > 0) {
72
+ malformed += 1;
73
+ deviant.push(preamble);
74
+ }
75
+ for (let s = 0; s < starts.length; s++) {
76
+ const startLine = starts[s];
77
+ const block = lines.slice(startLine, starts[s + 1] ?? lines.length);
78
+ const entry = parseBlock(block);
79
+ if (entry === null) {
80
+ malformed += 1;
81
+ deviant.push(block.join('\n'));
82
+ }
83
+ else {
84
+ entries.push(entry);
85
+ }
86
+ }
87
+ return { entries, malformed, deviant };
88
+ }
89
+ function parseBlock(block) {
90
+ const id = bareValue((block[0] ?? '').slice('- id:'.length));
91
+ const fields = {};
92
+ for (const line of block.slice(1)) {
93
+ const m = FIELD_PATTERN.exec(line);
94
+ if (m?.[1] !== undefined && m[2] !== undefined && !(m[1] in fields)) {
95
+ fields[m[1]] = unquote(m[2]);
96
+ }
97
+ }
98
+ const { kind, description } = fields;
99
+ if (!ID_PATTERN.test(id) || kind === undefined || !(kind in OBSERVATION_KINDS)) {
100
+ return null;
101
+ }
102
+ if (description === undefined || description.length === 0) {
103
+ return null;
104
+ }
105
+ return {
106
+ id,
107
+ kind,
108
+ description,
109
+ ...(fields.target !== undefined && { target: fields.target }),
110
+ ...(fields.severity !== undefined && { severity: fields.severity }),
111
+ ...(fields.workaround !== undefined && { workaround: fields.workaround }),
112
+ ...(fields.suggested_encoding !== undefined && {
113
+ suggested_encoding: fields.suggested_encoding,
114
+ }),
115
+ ...(fields.first_seen_at !== undefined && { first_seen_at: fields.first_seen_at }),
116
+ };
117
+ }
118
+ /** Quoted values are JSON strings; bare values may carry the old template's inline `# comments`. */
119
+ function unquote(raw) {
120
+ const trimmed = raw.trim();
121
+ if (trimmed.startsWith('"')) {
122
+ const m = /^"((?:[^"\\]|\\.)*)"/.exec(trimmed);
123
+ if (m !== null) {
124
+ try {
125
+ return JSON.parse(`"${m[1]}"`);
126
+ }
127
+ catch {
128
+ return m[1] ?? '';
129
+ }
130
+ }
131
+ return trimmed;
132
+ }
133
+ return bareValue(trimmed);
134
+ }
135
+ /** Strip an unquoted trailing ` # comment` (the old skill's template carried them inline). */
136
+ function bareValue(raw) {
137
+ return (raw.split(/\s+#/)[0] ?? raw).trim();
138
+ }
139
+ //# sourceMappingURL=buffer-codec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buffer-codec.js","sourceRoot":"","sources":["../../../src/services/observe/buffer-codec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ import type { Clock } from '../../adapters/clock/clock-port.js';
2
+ import type { EnvPort } from '../../adapters/env/env-port.js';
3
+ import type { FsPort } from '../../adapters/fs/fs-port.js';
4
+ import type { ProcessPort } from '../../adapters/process/process-port.js';
5
+ import { type ObservationEntry } from './buffer-codec.js';
6
+ /**
7
+ * Pure capture/list/clear logic for `harness observe` behind injected ports
8
+ * (P2/P3) — the deterministic half of in-flight friction capture the old
9
+ * observe skill made agents re-infer every session. The CLI owns: agent
10
+ * identity resolution (D4), schema validation at write (D6), per-kind
11
+ * sequential IDs over a tolerant parse (D2/D3), append-only buffer writes,
12
+ * and the `ensureTemp()` gitignore guarantee at capture time (AC-5).
13
+ */
14
+ export interface ObserveDeps {
15
+ fs: FsPort;
16
+ clock: Clock;
17
+ proc: ProcessPort;
18
+ env: EnvPort;
19
+ }
20
+ export interface CaptureOptions {
21
+ description?: string;
22
+ kind?: string;
23
+ target?: string;
24
+ severity?: string;
25
+ workaround?: string;
26
+ suggestedEncoding?: string;
27
+ agent?: string;
28
+ }
29
+ export interface ObserveFailure {
30
+ ok: false;
31
+ status: 'error' | 'unconfigured';
32
+ code?: string;
33
+ message: string;
34
+ next_action: string;
35
+ }
36
+ export type CaptureOutcome = {
37
+ ok: true;
38
+ bucket: string;
39
+ id: string;
40
+ kind: string;
41
+ path: string;
42
+ } | ObserveFailure;
43
+ /** One pending observation, annotated with the bucket it came from (D9). */
44
+ export interface AnnotatedEntry extends ObservationEntry {
45
+ bucket: string;
46
+ }
47
+ export type ListOutcome = {
48
+ ok: true;
49
+ observations: AnnotatedEntry[];
50
+ buckets_scanned: string[];
51
+ malformed_skipped: number;
52
+ } | ObserveFailure;
53
+ export type ClearOutcome = {
54
+ ok: true;
55
+ cleared: number;
56
+ buckets_scanned: string[];
57
+ malformed_skipped: number;
58
+ } | ObserveFailure;
59
+ /**
60
+ * Resolve the buffer bucket (D4, extending spec D-11): `--agent` flag →
61
+ * `HARNESS_AGENT` env → the literal `agent`. Each candidate is kebab-sanitized;
62
+ * an empty or whitespace-only value is treated as unset and falls through the
63
+ * chain — capture NEVER fails on identity (it's provenance labeling, not
64
+ * routing ceremony).
65
+ */
66
+ export declare function resolveBucket(agentFlag: string | undefined, env: EnvPort): string;
67
+ /** Capture ONE observation: validate → resolve identity → assign ID → append. */
68
+ export declare function captureObservation(opts: CaptureOptions, deps: ObserveDeps): CaptureOutcome;
69
+ /**
70
+ * List pending observations — ALL buckets by default (the sweep that keeps any
71
+ * bucket from stranding, D-12), `--agent` narrowing to one. Entries come back
72
+ * verbatim, annotated with their bucket; deviant blocks are counted in
73
+ * `malformed_skipped` and preserved on disk (D3/D9).
74
+ */
75
+ export declare function listObservations(opts: {
76
+ agent?: string;
77
+ }, deps: ObserveDeps): ListOutcome;
78
+ /**
79
+ * Remove what `--list` would return (all buckets by default, `--agent`-scoped;
80
+ * files kept). `cleared` counts the valid entries removed. Deviant blocks are
81
+ * NOT destroyed: they are written back in place (and reported in
82
+ * `malformed_skipped`) so hand-written-but-unparseable text survives the drain
83
+ * for manual inspection (D3 — nothing is silently dropped, companion F001).
84
+ */
85
+ export declare function clearObservations(opts: {
86
+ agent?: string;
87
+ }, deps: ObserveDeps): ClearOutcome;
@@ -0,0 +1,221 @@
1
+ import { ErrorCodes } from '../../output/error-codes.js';
2
+ import { posixJoin, toPosix } from '../shared/posix-path.js';
3
+ import { ensureTemp, HARNESS_DIR, TEMP_DIR } from '../shared/temp.js';
4
+ import { OBSERVATION_KINDS, OBSERVATION_SEVERITIES, parseBuffer, serializeEntry, } from './buffer-codec.js';
5
+ const BUFFER_FILE = 'session-buffer.md';
6
+ const DEFAULT_BUCKET = 'agent';
7
+ const MIN_DESCRIPTION = 10;
8
+ /** Lowercase-kebab the raw identity; empties out rather than failing (D4). */
9
+ function sanitizeBucket(raw) {
10
+ return raw
11
+ .trim()
12
+ .toLowerCase()
13
+ .replace(/[^a-z0-9]+/g, '-')
14
+ .replace(/^-|-$/g, '');
15
+ }
16
+ /**
17
+ * Resolve the buffer bucket (D4, extending spec D-11): `--agent` flag →
18
+ * `HARNESS_AGENT` env → the literal `agent`. Each candidate is kebab-sanitized;
19
+ * an empty or whitespace-only value is treated as unset and falls through the
20
+ * chain — capture NEVER fails on identity (it's provenance labeling, not
21
+ * routing ceremony).
22
+ */
23
+ export function resolveBucket(agentFlag, env) {
24
+ for (const candidate of [agentFlag, env.get('HARNESS_AGENT')]) {
25
+ if (candidate !== undefined) {
26
+ const cleaned = sanitizeBucket(candidate);
27
+ if (cleaned.length > 0)
28
+ return cleaned;
29
+ }
30
+ }
31
+ return DEFAULT_BUCKET;
32
+ }
33
+ /** Capture ONE observation: validate → resolve identity → assign ID → append. */
34
+ export function captureObservation(opts, deps) {
35
+ const cwd = toPosix(deps.proc.cwd());
36
+ if (!deps.fs.exists(posixJoin(cwd, HARNESS_DIR))) {
37
+ return unconfiguredRepo(cwd);
38
+ }
39
+ // Validation at write (D6): reject before any side effect, buffer untouched.
40
+ const kind = opts.kind ?? '';
41
+ const kinds = Object.keys(OBSERVATION_KINDS);
42
+ if (!kinds.includes(kind)) {
43
+ return {
44
+ ok: false,
45
+ status: 'unconfigured',
46
+ message: `Unknown --kind ${JSON.stringify(kind)}.`,
47
+ next_action: `Pass --kind as one of: ${kinds.join(', ')}.`,
48
+ };
49
+ }
50
+ const description = opts.description ?? '';
51
+ if (description.length < MIN_DESCRIPTION) {
52
+ return {
53
+ ok: false,
54
+ status: 'unconfigured',
55
+ message: `Description too short (${description.length} chars).`,
56
+ next_action: `Describe the observation in at least ${MIN_DESCRIPTION} characters (got ${description.length}).`,
57
+ };
58
+ }
59
+ if (opts.severity !== undefined &&
60
+ !OBSERVATION_SEVERITIES.includes(opts.severity)) {
61
+ return {
62
+ ok: false,
63
+ status: 'unconfigured',
64
+ message: `Unknown --severity ${JSON.stringify(opts.severity)}.`,
65
+ next_action: `Pass --severity as one of: ${OBSERVATION_SEVERITIES.join(', ')}.`,
66
+ };
67
+ }
68
+ const bucket = resolveBucket(opts.agent, deps.env);
69
+ const bucketDir = posixJoin(cwd, HARNESS_DIR, TEMP_DIR, bucket);
70
+ const bufferAbs = posixJoin(bucketDir, BUFFER_FILE);
71
+ const relPath = posixJoin(HARNESS_DIR, TEMP_DIR, bucket, BUFFER_FILE);
72
+ let existing = '';
73
+ if (deps.fs.exists(bufferAbs)) {
74
+ const text = deps.fs.readText(bufferAbs);
75
+ if (text === null) {
76
+ return unreadableBuffer(relPath);
77
+ }
78
+ existing = text;
79
+ }
80
+ // Per-kind sequential ID over the VALID entries only — malformed blocks are
81
+ // skipped by the scan and the buffer is never rewritten (AC-3).
82
+ const prefix = OBSERVATION_KINDS[kind];
83
+ const id = nextId(prefix, parseBuffer(existing).entries);
84
+ const entry = {
85
+ id,
86
+ kind,
87
+ description,
88
+ ...(opts.target !== undefined && { target: opts.target }),
89
+ ...(opts.severity !== undefined && { severity: opts.severity }),
90
+ ...(opts.workaround !== undefined && { workaround: opts.workaround }),
91
+ ...(opts.suggestedEncoding !== undefined && { suggested_encoding: opts.suggestedEncoding }),
92
+ first_seen_at: deps.clock.nowIso(),
93
+ };
94
+ try {
95
+ ensureTemp(deps); // the transient guarantee fires at capture time (AC-5)
96
+ deps.fs.mkdirp(bucketDir);
97
+ const separator = existing.length > 0 && !existing.endsWith('\n') ? '\n' : '';
98
+ deps.fs.writeText(bufferAbs, existing + separator + serializeEntry(entry));
99
+ }
100
+ catch (err) {
101
+ return {
102
+ ok: false,
103
+ status: 'error',
104
+ code: ErrorCodes.UNKNOWN,
105
+ message: `Could not write ${relPath}: ${err instanceof Error ? err.message : String(err)}`,
106
+ next_action: `Could not write \`${relPath}\` (permissions?). Check \`${HARNESS_DIR}/\` is writable.`,
107
+ };
108
+ }
109
+ return { ok: true, bucket, id, kind, path: relPath };
110
+ }
111
+ /**
112
+ * List pending observations — ALL buckets by default (the sweep that keeps any
113
+ * bucket from stranding, D-12), `--agent` narrowing to one. Entries come back
114
+ * verbatim, annotated with their bucket; deviant blocks are counted in
115
+ * `malformed_skipped` and preserved on disk (D3/D9).
116
+ */
117
+ export function listObservations(opts, deps) {
118
+ const swept = sweepBuckets(opts, deps);
119
+ if ('failure' in swept)
120
+ return swept.failure;
121
+ const observations = [];
122
+ let malformed = 0;
123
+ for (const bucket of swept.buckets) {
124
+ const { entries, malformed: m } = parseBuffer(bucket.content);
125
+ malformed += m;
126
+ for (const entry of entries) {
127
+ observations.push({ ...entry, bucket: bucket.name });
128
+ }
129
+ }
130
+ return {
131
+ ok: true,
132
+ observations,
133
+ buckets_scanned: swept.buckets.map((b) => b.name),
134
+ malformed_skipped: malformed,
135
+ };
136
+ }
137
+ /**
138
+ * Remove what `--list` would return (all buckets by default, `--agent`-scoped;
139
+ * files kept). `cleared` counts the valid entries removed. Deviant blocks are
140
+ * NOT destroyed: they are written back in place (and reported in
141
+ * `malformed_skipped`) so hand-written-but-unparseable text survives the drain
142
+ * for manual inspection (D3 — nothing is silently dropped, companion F001).
143
+ */
144
+ export function clearObservations(opts, deps) {
145
+ const swept = sweepBuckets(opts, deps);
146
+ if ('failure' in swept)
147
+ return swept.failure;
148
+ let cleared = 0;
149
+ let malformed = 0;
150
+ for (const bucket of swept.buckets) {
151
+ const { entries, malformed: m, deviant } = parseBuffer(bucket.content);
152
+ cleared += entries.length;
153
+ malformed += m;
154
+ const kept = deviant.map((chunk) => (chunk.endsWith('\n') ? chunk : `${chunk}\n`)).join('');
155
+ deps.fs.writeText(bucket.bufferAbs, kept);
156
+ }
157
+ return {
158
+ ok: true,
159
+ cleared,
160
+ buckets_scanned: swept.buckets.map((b) => b.name),
161
+ malformed_skipped: malformed,
162
+ };
163
+ }
164
+ /**
165
+ * Resolve which buckets to operate on and read their buffers up front, so an
166
+ * unreadable buffer mid-sweep fails the whole command honestly (E146, D6)
167
+ * before any truncation happens.
168
+ */
169
+ function sweepBuckets(opts, deps) {
170
+ const cwd = toPosix(deps.proc.cwd());
171
+ if (!deps.fs.exists(posixJoin(cwd, HARNESS_DIR))) {
172
+ return { failure: unconfiguredRepo(cwd) };
173
+ }
174
+ const tempDir = posixJoin(cwd, HARNESS_DIR, TEMP_DIR);
175
+ const scoped = opts.agent !== undefined ? sanitizeBucket(opts.agent) : undefined;
176
+ const candidates = scoped !== undefined && scoped.length > 0 ? [scoped] : deps.fs.readdir(tempDir).slice().sort();
177
+ const buckets = [];
178
+ for (const name of candidates) {
179
+ const bufferAbs = posixJoin(tempDir, name, BUFFER_FILE);
180
+ if (!deps.fs.exists(bufferAbs)) {
181
+ continue; // not a bucket (e.g. the nested .gitignore) or nothing captured yet
182
+ }
183
+ const content = deps.fs.readText(bufferAbs);
184
+ if (content === null) {
185
+ return { failure: unreadableBuffer(posixJoin(HARNESS_DIR, TEMP_DIR, name, BUFFER_FILE)) };
186
+ }
187
+ buckets.push({ name, bufferAbs, content });
188
+ }
189
+ return { buckets };
190
+ }
191
+ function nextId(prefix, entries) {
192
+ let max = 0;
193
+ const pattern = new RegExp(`^${prefix}-(\\d+)$`);
194
+ for (const entry of entries) {
195
+ const m = pattern.exec(entry.id);
196
+ if (m?.[1] !== undefined) {
197
+ const n = Number.parseInt(m[1], 10);
198
+ if (n > max)
199
+ max = n;
200
+ }
201
+ }
202
+ return `${prefix}-${String(max + 1).padStart(3, '0')}`;
203
+ }
204
+ function unconfiguredRepo(cwd) {
205
+ return {
206
+ ok: false,
207
+ status: 'unconfigured',
208
+ message: `No ${HARNESS_DIR}/ directory found in ${cwd}.`,
209
+ next_action: `No \`${HARNESS_DIR}/\` here — set up the harness first (create \`${HARNESS_DIR}/\`), then re-run.`,
210
+ };
211
+ }
212
+ function unreadableBuffer(relPath) {
213
+ return {
214
+ ok: false,
215
+ status: 'error',
216
+ code: ErrorCodes.OBSERVE_BUFFER_UNREADABLE,
217
+ message: `Observation buffer ${relPath} exists but could not be read.`,
218
+ next_action: `Inspect \`${relPath}\` — fix permissions or move it aside, then retry (the CLI never deletes it).`,
219
+ };
220
+ }
221
+ //# sourceMappingURL=observe-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observe-service.js","sourceRoot":"","sources":["../../../src/services/observe/observe-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * PUBLIC record-type contract — the type an extension author imports to declare a
3
+ * new kind of record (alongside the verb contract).
4
+ *
5
+ * Authors `import type { HarnessRecordType } from '@ai-substrate/engineering-harness/contract'`
6
+ * (re-exported from `services/extensions/contract.ts`, which the package's
7
+ * `exports["./contract"]` map resolves). Everything here is **types only**, so it
8
+ * is erased at runtime — even a plain `.js` record extension can reference it via
9
+ * JSDoc with no runtime dependency on the core.
10
+ *
11
+ * The CLI is deliberately **schema-agnostic**: it knows only these four fields and
12
+ * writes the `template` to a path. The record's *actual* schema lives INSIDE the
13
+ * `template` body (frontmatter keys + commented guidance), so adding the next
14
+ * record type is "a template + four fields", never a change to `harness record`.
15
+ */
16
+ /** A record type: a named template the `record` command scaffolds into `.harness/records/<type>/`. */
17
+ export interface HarnessRecordType {
18
+ /** Discriminator — distinguishes a record-type export from a verb export. REQUIRED. */
19
+ kind: 'record';
20
+ /**
21
+ * The `<type>` arg + the `.harness/records/<type>/` directory name.
22
+ * Pattern: `^[a-z][a-z0-9-]*$` (same rule as verb names).
23
+ */
24
+ type: string;
25
+ /** One line — shown by `harness record --list` and `doctor`. */
26
+ description: string;
27
+ /**
28
+ * The file body the agent fills. The record's "schema" lives HERE as
29
+ * frontmatter + commented field guidance — the CLI never parses it.
30
+ */
31
+ template: string;
32
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * PUBLIC record-type contract — the type an extension author imports to declare a
3
+ * new kind of record (alongside the verb contract).
4
+ *
5
+ * Authors `import type { HarnessRecordType } from '@ai-substrate/engineering-harness/contract'`
6
+ * (re-exported from `services/extensions/contract.ts`, which the package's
7
+ * `exports["./contract"]` map resolves). Everything here is **types only**, so it
8
+ * is erased at runtime — even a plain `.js` record extension can reference it via
9
+ * JSDoc with no runtime dependency on the core.
10
+ *
11
+ * The CLI is deliberately **schema-agnostic**: it knows only these four fields and
12
+ * writes the `template` to a path. The record's *actual* schema lives INSIDE the
13
+ * `template` body (frontmatter keys + commented guidance), so adding the next
14
+ * record type is "a template + four fields", never a change to `harness record`.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/services/record/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}