@awebai/oats 0.29.4 → 0.30.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 (224) hide show
  1. package/README.md +12 -6
  2. package/bin/oats.mjs +194 -50
  3. package/capabilities/oats-aweb/bin/oats-aweb.mjs +538 -204
  4. package/capabilities/oats-aweb/injects/aweb.md +1 -1
  5. package/capabilities/oats-aweb/lib/binding-wire.mjs +31 -22
  6. package/capabilities/oats-aweb/oats.json +5 -12
  7. package/capabilities/oats-aweb/skills/VENDORED.md +4 -4
  8. package/capabilities/oats-aweb/skills/aweb-team-membership/SKILL.md +1 -1
  9. package/capabilities/oats-aweb/skills/oats-aweb/SKILL.md +83 -13
  10. package/capabilities/oats-code-review/injects/reviewer.md +26 -0
  11. package/capabilities/oats-code-review/oats.json +16 -0
  12. package/capabilities/oats-code-review/skills/adversarial-review/SKILL.md +66 -0
  13. package/capabilities/oats-code-review/skills/review-dev-docs/SKILL.md +30 -0
  14. package/capabilities/oats-code-review/skills/security-review/SKILL.md +56 -0
  15. package/capabilities/oats-code-review/skills/simplification-review/SKILL.md +34 -0
  16. package/capabilities/oats-developer/injects/developer.md +38 -0
  17. package/capabilities/oats-developer/oats.json +17 -0
  18. package/capabilities/oats-developer/skills/execution-strategy/SKILL.md +43 -0
  19. package/capabilities/oats-developer/skills/maintain-dev-docs/SKILL.md +47 -0
  20. package/capabilities/oats-developer/skills/run-the-review-loop/SKILL.md +65 -0
  21. package/capabilities/oats-developer/skills/understand-the-spec/SKILL.md +37 -0
  22. package/capabilities/oats-developer/skills/worktrees/SKILL.md +36 -0
  23. package/capabilities/oats-engineering-expert/injects/expert.md +37 -0
  24. package/capabilities/oats-engineering-expert/oats.json +17 -0
  25. package/capabilities/oats-engineering-expert/skills/coordinate-developers/SKILL.md +37 -0
  26. package/capabilities/oats-engineering-expert/skills/coordinate-experts/SKILL.md +52 -0
  27. package/capabilities/oats-engineering-expert/skills/land-your-prs/SKILL.md +50 -0
  28. package/capabilities/oats-engineering-expert/skills/plan-and-spec/SKILL.md +53 -0
  29. package/capabilities/oats-engineering-expert/skills/verify-developer-work/SKILL.md +49 -0
  30. package/capabilities/oats-okf/bin/oats-okf.mjs +8 -4
  31. package/capabilities/oats-okf/lib/binding-wire.mjs +47 -15
  32. package/capabilities/oats-okf/lib/inspection.mjs +26 -7
  33. package/capabilities/oats-okf/lib/sources.mjs +16 -2
  34. package/capabilities/oats-okf/lib/worker.mjs +5 -16
  35. package/capabilities/oats-okf/oats.json +6 -3
  36. package/capabilities/oats-okf-harvest/bin/okf-harvest.mjs +2 -2
  37. package/capabilities/oats-okf-harvest/oats.json +3 -3
  38. package/capabilities/oats-okf-harvest/skills/knowledge-harvest/SKILL.md +6 -6
  39. package/capabilities/oats-okf-maintenance/bin/okf-maintenance.mjs +2 -2
  40. package/capabilities/oats-okf-maintenance/injects/maintainer.md +1 -1
  41. package/capabilities/oats-okf-maintenance/oats.json +2 -2
  42. package/capabilities/oats-okf-maintenance/skills/knowledge-review/SKILL.md +1 -1
  43. package/capabilities/oats-okf-maintenance/skills/okf-trigger-setup/SKILL.md +11 -23
  44. package/capabilities/oats-workspace-experts/injects/oats-experts.md +26 -0
  45. package/capabilities/oats-workspace-experts/oats.json +9 -0
  46. package/docs/capabilities.md +160 -171
  47. package/docs/capability-manifest.schema.json +6 -11
  48. package/docs/configuration.md +213 -64
  49. package/docs/design/2026-09-16-knowledge-capability-contract.md +36 -50
  50. package/docs/design/2026-09-23-workspace-module-contracts.md +377 -544
  51. package/docs/design/2026-09-26-okf-knowledge-operations.md +132 -357
  52. package/docs/design/2026-09-27-team-model-v2.md +97 -117
  53. package/docs/design/2026-09-28-automations-trust.md +38 -0
  54. package/docs/design/2026-09-28-soul-launch-preference.md +63 -0
  55. package/docs/design/HISTORY.md +65 -0
  56. package/docs/design/README.md +23 -54
  57. package/docs/desktop-cli-api.md +1787 -1777
  58. package/docs/desktop.md +30 -91
  59. package/docs/execution-targets.md +146 -292
  60. package/docs/first-team.md +31 -17
  61. package/docs/implementation.md +76 -288
  62. package/docs/integrations.md +118 -320
  63. package/docs/knowledge-capability-authoring.md +25 -52
  64. package/docs/knowledge-reference/acceptance.md +3 -3
  65. package/docs/knowledge-reference/adoption.md +1 -1
  66. package/docs/knowledge-reference/harvester.md +2 -2
  67. package/docs/knowledge-reference/package-craft.md +3 -3
  68. package/docs/knowledge-reference/provider-mapping.md +3 -6
  69. package/docs/knowledge-reference/reader-capture.md +3 -3
  70. package/docs/knowledge-theory.md +62 -166
  71. package/docs/knowledge.md +225 -404
  72. package/docs/layers.md +42 -97
  73. package/docs/oats-local.schema.json +58 -5
  74. package/docs/oats-membership.schema.json +1 -8
  75. package/docs/oats-package.schema.json +5 -5
  76. package/docs/oats-workspace.schema.json +8 -22
  77. package/docs/official-catalog.md +25 -28
  78. package/docs/packages.md +45 -63
  79. package/docs/plans/0.30-close-out.md +61 -0
  80. package/docs/release-lane.md +77 -0
  81. package/docs/release-notes/oats-framework-v1.1.3.md +10 -8
  82. package/docs/release-notes/v0.19.0.md +48 -147
  83. package/docs/release-notes/v0.19.1.md +2 -3
  84. package/docs/release-notes/v0.19.3.md +2 -15
  85. package/docs/release-notes/v0.20.0.md +0 -15
  86. package/docs/release-notes/v0.22.0.md +71 -138
  87. package/docs/release-notes/v0.22.1.md +42 -90
  88. package/docs/release-notes/v0.22.10.md +1 -1
  89. package/docs/release-notes/v0.22.11.md +1 -47
  90. package/docs/release-notes/v0.22.12.md +4 -13
  91. package/docs/release-notes/v0.22.13.md +1 -42
  92. package/docs/release-notes/v0.22.14.md +3 -11
  93. package/docs/release-notes/v0.22.15.md +1 -46
  94. package/docs/release-notes/v0.22.16.md +6 -8
  95. package/docs/release-notes/v0.22.18.md +1 -99
  96. package/docs/release-notes/v0.22.19.md +3 -14
  97. package/docs/release-notes/v0.22.2.md +6 -15
  98. package/docs/release-notes/v0.22.3.md +0 -1
  99. package/docs/release-notes/v0.22.4.md +1 -14
  100. package/docs/release-notes/v0.22.5.md +2 -12
  101. package/docs/release-notes/v0.22.6.md +0 -3
  102. package/docs/release-notes/v0.23.0.md +9 -25
  103. package/docs/release-notes/v0.23.1.md +9 -25
  104. package/docs/release-notes/v0.23.2.md +2 -4
  105. package/docs/release-notes/v0.24.0.md +56 -97
  106. package/docs/release-notes/v0.24.1.md +7 -11
  107. package/docs/release-notes/v0.24.10.md +34 -45
  108. package/docs/release-notes/v0.24.11.md +12 -20
  109. package/docs/release-notes/v0.24.12.md +35 -48
  110. package/docs/release-notes/v0.24.13.md +34 -41
  111. package/docs/release-notes/v0.24.2.md +9 -13
  112. package/docs/release-notes/v0.24.3.md +7 -11
  113. package/docs/release-notes/v0.24.4.md +6 -6
  114. package/docs/release-notes/v0.24.5.md +6 -10
  115. package/docs/release-notes/v0.24.6.md +2 -5
  116. package/docs/release-notes/v0.24.7.md +46 -75
  117. package/docs/release-notes/v0.24.8.md +58 -96
  118. package/docs/release-notes/v0.24.9.md +38 -54
  119. package/docs/release-notes/v0.25.0.md +59 -76
  120. package/docs/release-notes/v0.25.1.md +57 -81
  121. package/docs/release-notes/v0.25.2.md +51 -70
  122. package/docs/release-notes/v0.25.3.md +11 -13
  123. package/docs/release-notes/v0.25.4.md +9 -13
  124. package/docs/release-notes/v0.25.5.md +3 -5
  125. package/docs/release-notes/v0.25.6.md +20 -29
  126. package/docs/release-notes/v0.25.7.md +5 -7
  127. package/docs/release-notes/v0.25.8.md +26 -39
  128. package/docs/release-notes/v0.26.0.md +175 -646
  129. package/docs/release-notes/v0.27.0.md +4 -5
  130. package/docs/release-notes/v0.27.1.md +4 -6
  131. package/docs/release-notes/v0.27.2.md +1 -1
  132. package/docs/release-notes/v0.28.0.md +57 -124
  133. package/docs/release-notes/v0.29.0.md +89 -208
  134. package/docs/release-notes/v0.29.1.md +1 -1
  135. package/docs/release-notes/v0.29.2.md +3 -4
  136. package/docs/release-notes/v0.30.0.md +205 -0
  137. package/docs/schedules.md +280 -363
  138. package/docs/servers.md +99 -117
  139. package/docs/soul.schema.json +2 -9
  140. package/docs/souls-and-instances.md +145 -158
  141. package/docs/workspaces.md +132 -215
  142. package/lib/automations.mjs +21 -6
  143. package/lib/core.mjs +226 -74
  144. package/lib/instance-events.mjs +1 -1
  145. package/lib/instance-inspect.mjs +109 -34
  146. package/lib/instance-lifecycle.mjs +14 -1
  147. package/lib/instance-resolution.mjs +26 -27
  148. package/lib/launch-preference.mjs +87 -0
  149. package/lib/materialize.mjs +3 -3
  150. package/lib/resolve.mjs +29 -87
  151. package/lib/schedule.mjs +1 -1
  152. package/lib/teams-verbs.mjs +195 -0
  153. package/lib/teams.mjs +190 -0
  154. package/lib/triggers.mjs +2 -2
  155. package/lib/workspace.mjs +54 -147
  156. package/package-catalog.json +9 -15
  157. package/package.json +1 -1
  158. package/skills/oats-getting-started/SKILL.md +25 -13
  159. package/capabilities/oats-review/injects/review.md +0 -69
  160. package/capabilities/oats-review/oats.json +0 -10
  161. package/capabilities/oats-review/skills/code-review/SKILL.md +0 -44
  162. package/capabilities/oats-review/skills/security-review/SKILL.md +0 -59
  163. package/docs/conventions.md +0 -90
  164. package/docs/design/2026-09-07-architecture-reassessment.md +0 -131
  165. package/docs/design/2026-09-07-desktop-souls-capabilities.md +0 -50
  166. package/docs/design/2026-09-07-mobile-agent-management-proposal.md +0 -228
  167. package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +0 -558
  168. package/docs/design/2026-09-13-knowledge-and-memory-direction.md +0 -744
  169. package/docs/design/2026-09-13-knowledge-implementation.md +0 -127
  170. package/docs/design/2026-09-13-knowledge-location-contract.md +0 -340
  171. package/docs/design/2026-09-14-artifact-retention-contract.md +0 -190
  172. package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +0 -708
  173. package/docs/design/2026-09-14-portable-souls-contract-amendments.md +0 -85
  174. package/docs/design/2026-09-14-portable-souls-explainer.md +0 -750
  175. package/docs/design/2026-09-15-captured-dispatch.md +0 -127
  176. package/docs/design/2026-09-15-captured-resolution-records.md +0 -143
  177. package/docs/design/2026-09-15-package-preparation.md +0 -100
  178. package/docs/design/2026-09-15-portable-data-contract.md +0 -121
  179. package/docs/design/2026-09-15-portable-declarations.md +0 -189
  180. package/docs/design/2026-09-15-portable-souls-handoff.md +0 -150
  181. package/docs/design/2026-09-15-portable-souls-implementation.md +0 -417
  182. package/docs/design/2026-09-15-selection-lock-and-approval.md +0 -122
  183. package/docs/design/2026-09-15-source-observation.md +0 -119
  184. package/docs/design/2026-09-16-captured-admission.md +0 -77
  185. package/docs/design/2026-09-16-captured-helper-dispatch.md +0 -105
  186. package/docs/design/2026-09-16-captured-launch-inputs.md +0 -42
  187. package/docs/design/2026-09-16-command-profile-preparation.md +0 -86
  188. package/docs/design/2026-09-16-fresh-install-first-rollout.md +0 -47
  189. package/docs/design/2026-09-16-fresh-operator-walkthrough.md +0 -282
  190. package/docs/design/2026-09-16-messaging-capability-contract.md +0 -59
  191. package/docs/design/2026-09-16-portable-migration-evidence.md +0 -158
  192. package/docs/design/2026-09-16-portable-onboarding.md +0 -179
  193. package/docs/design/2026-09-16-prepare-request-transport.md +0 -26
  194. package/docs/design/2026-09-16-provider-binding-codecs.md +0 -98
  195. package/docs/design/2026-09-16-provider-binding-wire.md +0 -274
  196. package/docs/design/2026-09-17-capability-helper-input-contract.md +0 -95
  197. package/docs/design/2026-09-17-captured-backend-parity.md +0 -53
  198. package/docs/design/2026-09-17-captured-native-start.md +0 -58
  199. package/docs/design/2026-09-17-portable-boundary-hookup.md +0 -19
  200. package/docs/design/2026-09-17-portable-boundary-resources.md +0 -52
  201. package/docs/design/2026-09-17-public-captured-start.md +0 -108
  202. package/docs/design/2026-09-17-public-prepare-request.md +0 -90
  203. package/docs/design/2026-09-18-captured-pi-host.md +0 -205
  204. package/docs/design/2026-09-18-first-cut-release-checklist.md +0 -131
  205. package/docs/design/2026-09-18-herdr-protocol-compatibility.md +0 -60
  206. package/docs/design/2026-09-20-redesign-program-board.md +0 -142
  207. package/docs/design/2026-09-20-workspace-and-portable-adoption-plan.md +0 -289
  208. package/docs/design/2026-09-20-workspace-onboarding-public.md +0 -207
  209. package/docs/design/2026-09-22-desktop-parity-seams.md +0 -58
  210. package/docs/design/2026-09-23-simplified-workspace-model.md +0 -711
  211. package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +0 -65
  212. package/docs/design/2026-09-24-desktop-phase-f-boundary.md +0 -242
  213. package/docs/design/2026-09-24-phase-d-plan.md +0 -305
  214. package/docs/design/2026-09-25-teams-contract.md +0 -258
  215. package/docs/design/2026-09-26-desktop-design-brief-architecture.md +0 -241
  216. package/docs/design/desktop-ux-plan.md +0 -362
  217. package/docs/design/launch-configurations.md +0 -168
  218. package/docs/design/okf-mirror-provenance.md +0 -105
  219. package/docs/design/operations-contract.md +0 -141
  220. package/docs/oats-member.schema.json +0 -38
  221. package/skills/integration-authoring/SKILL.md +0 -84
  222. package/skills/oats-support/SKILL.md +0 -79
  223. package/skills/skill-craft/SKILL.md +0 -109
  224. package/skills/soul-craft/SKILL.md +0 -116
@@ -1,5 +1,4 @@
1
1
  import { TextDecoder } from 'node:util';
2
- import { validateInvocationShape } from './invocation-shape.mjs';
3
2
  import { tmpdir } from 'node:os';
4
3
  import { isAbsolute, join, resolve } from 'node:path';
5
4
  import { fs, safePath } from './io.mjs';
@@ -14,7 +13,7 @@ import {
14
13
  renderKnowledgeRuntime,
15
14
  sameJson,
16
15
  } from './portable-binding.mjs';
17
- import { validateBindings } from './config.mjs';
16
+ import { declaration as soulDeclaration, loadBindings, validateBindings } from './config.mjs';
18
17
  import { stageBase, validateBase } from './stores.mjs';
19
18
 
20
19
  export const BINDING_WIRE_LIMITS=Object.freeze({bytes:1024*1024,depth:32,entries:16384});
@@ -254,14 +253,20 @@ export function loadInvocationKnowledgeBinding(env=process.env) {
254
253
  // binding.reasons (byte-exact) — the manifest test pins that.
255
254
  const checkReasons=Object.freeze({
256
255
  'action:not-admitted':'check action is not an admitted knowledge operation',
257
- 'bindings:invalid':'bound runtime bindings file is missing or invalid',
256
+ 'settings:missing':'OKF settings missing bindings-file: set settings.oats.okf bindings-file, or deactivate oats.okf',
257
+ 'soul:missing':'OKF soul declaration missing: create okf.json for this soul, or deactivate oats.okf',
258
+ 'harvest-runtime:missing':settingMessages['harvest-runtime:missing'],
259
+ 'harvest-runtime:invalid':settingMessages['harvest-runtime:invalid'],
260
+ 'harvest-model:invalid':settingMessages['harvest-model:invalid'],
261
+ 'binding:not-configured':'OKF binding missing or invalid: provision soul OKF declaration and bindings file, or deactivate oats.okf',
262
+ 'bindings:invalid':'OKF bindings file missing or invalid: repair configured bindings file, or deactivate oats.okf',
258
263
  'bases:too-many':'more than 64 git knowledge bases declared',
259
264
  'base:stage-failed':'declared knowledge base could not be staged from its git source',
260
265
  'base:not-validated':'declared knowledge base is not a validated knowledge tree',
261
266
  'base:owner-unmet':'knowledge base owner or remote custody requirement not met',
262
267
  'base:source-mismatch':'staged git source does not match the declared knowledge base',
263
268
  'runtime:command-missing':'harvest runtime command is not installed on this host',
264
- 'runtime:not-qualified':'harvest runtime could not be qualified against the accepted bases',
269
+ 'runtime:not-qualified':'harvest runtime is not qualified: install/configure the selected harvest runtime or set harvest off',
265
270
  });
266
271
  function problem(code,reason) {
267
272
  if(reason===undefined) return {code};
@@ -276,15 +281,40 @@ function providerActionName(action) {
276
281
  if(typeof action.name==='string' && action.name.startsWith('okf:')) return action.name.slice(4);
277
282
  return null;
278
283
  }
284
+ function readinessRuntime(settings,env=process.env) {
285
+ if(Object.hasOwn(settings,'bindings-file')) {
286
+ try {
287
+ if(typeof settings['bindings-file']!=='string' || !absolute(settings['bindings-file'])) return {problem:problem('needs-configuration','settings:missing')};
288
+ if(settings.harvest!==undefined && !['on','off'].includes(settings.harvest)) return {problem:problem('needs-configuration','runtime:not-qualified')};
289
+ if(settings.harvest!=='off') {
290
+ if(settings['harvest-runtime']===undefined) return {problem:problem('needs-configuration','harvest-runtime:missing')};
291
+ if(!['pi','claude','codex'].includes(settings['harvest-runtime'])) return {problem:problem('needs-configuration','harvest-runtime:invalid')};
292
+ if(settings['harvest-model']!==undefined && (typeof settings['harvest-model']!=='string' || !settings['harvest-model'].trim())) return {problem:problem('needs-configuration','harvest-model:invalid')};
293
+ }
294
+ const bindings=loadBindings(settings['bindings-file']);
295
+ if(!env.OATS_SOUL) return {problem:problem('needs-configuration','soul:missing')};
296
+ const decl=soulDeclaration(env.OATS_SOUL);
297
+ return {runtime:{descriptorFile:bindings.file,bindings:{version:1,stateDir:bindings.stateDir,bases:bindings.bases},declaration:decl}};
298
+ } catch(error) {
299
+ const message=String(error?.message || '');
300
+ if(error?.code==='E_CONFIG' && /okf\.json|soul has no/.test(message)) return {problem:problem('needs-configuration','soul:missing')};
301
+ return {problem:problem('needs-configuration','bindings:invalid')};
302
+ }
303
+ }
304
+ if(settings.schemaVersion!==undefined) {
305
+ try { return {runtime:bindingPayload(settings,{diagnoseSettings:true}).runtime}; }
306
+ catch(error) {if(['invalid-binding','needs-configuration'].includes(error?.wireCode)) return {problem:problem('needs-configuration','binding:not-configured')};throw error;}
307
+ }
308
+ return {problem:problem('needs-configuration','settings:missing')};
309
+ }
279
310
  function checkPhase(req) {
280
- keys(req.input,['binding','context','action','invocation'],['binding','context','action'],'check input');
311
+ keys(req.input,['context','action'],['context','action'],'check input');
281
312
  if(!obj(req.input.context) || !obj(req.input.action)) wireError('invalid-binding');
282
- if(Object.hasOwn(req.input,'invocation')) validateInvocationShape(req.input.invocation,{capability:CAPABILITY,context:req.input.context,action:req.input.action});
283
- const {runtime}=bindingPayload(req.input.binding,{diagnoseSettings:true}),action=req.input.action,name=providerActionName(action);
284
- const harvestInvocation=req.input.invocation;
285
- const admittedHarvest=name==='harvest' && action.kind==='operation' && action.slot==='knowledge' && action.name==='harvest'
286
- && harvestInvocation?.subject.kind==='persistent' && harvestInvocation.instance!==null && !!harvestInvocation.intent;
287
- if(name && (unsupportedCapturedCommands.has(name) || name==='run-source' || (name==='harvest' && !admittedHarvest))) return {status:'needs-configuration',problems:[problem('provider-not-qualified','action:not-admitted')]};
313
+ const action=req.input.action,name=providerActionName(action);
314
+ const prepared=readinessRuntime(req.settings);
315
+ if(prepared.problem) return {status:'needs-configuration',problems:[prepared.problem]};
316
+ const runtime=prepared.runtime;
317
+ if(name && (unsupportedCapturedCommands.has(name) || name==='run-source' || name==='harvest')) return {status:'needs-configuration',problems:[problem('provider-not-qualified','action:not-admitted')]};
288
318
  if(action.kind==='hook' && action.name==='soul-scaffold') return {status:'ready',problems:[]};
289
319
  let bindings;try{bindings=validateBindings(runtime.bindings,runtime.descriptorFile);}catch{return {status:'needs-configuration',problems:[problem('needs-configuration','bindings:invalid')]};}
290
320
  const accepted={},gitBases=Object.entries(bindings.bases).filter(([,base])=>base.kind==='git');
@@ -296,14 +326,16 @@ function checkPhase(req) {
296
326
  stage=base.kind==='directory'?'validate':'stage';
297
327
  accepted[alias]=(base.kind==='directory'?validateBase(base.path,base):stageBase(base,join(scratch,alias),{alias})).meta;
298
328
  }
299
- stage='runtime';
300
- checkKnowledgeRuntime({rendered:runtime,accepted});
329
+ if(req.settings.harvest!=='off') {
330
+ stage='runtime';
331
+ checkKnowledgeRuntime({rendered:runtime,accepted});
332
+ }
301
333
  } catch(error) {
302
- if(error.code==='E_COMMAND') return {status:'unavailable',problems:[problem('provider-unavailable','runtime:command-missing')]};
334
+ if(error.code==='E_COMMAND') return {status:'needs-configuration',problems:[problem('needs-configuration','runtime:command-missing')]};
303
335
  if(error.code==='E_OWNER') return {status:'needs-configuration',problems:[problem('provider-not-qualified','base:owner-unmet')]};
304
336
  if(error.code==='E_CONFIRM') return {status:'needs-configuration',problems:[problem('provider-not-qualified','base:source-mismatch')]};
305
337
  if(['E_BASE','E_VALIDATION','E_DIRECTORY_GIT'].includes(error.code)) return {status:'needs-configuration',problems:[problem('provider-not-qualified',stage==='stage'?'base:stage-failed':'base:not-validated')]};
306
- if(stage==='runtime') return {status:'unavailable',problems:[problem('provider-unavailable','runtime:not-qualified')]};
338
+ if(stage==='runtime') return {status:'needs-configuration',problems:[problem('needs-configuration','runtime:not-qualified')]};
307
339
  return {status:'unavailable',problems:[problem('provider-unavailable')]};
308
340
  } finally {
309
341
  if(scratch) fs.rmSync(scratch,{recursive:true,force:true});
@@ -53,10 +53,8 @@ function liveHome(source,status) {
53
53
  return {available:false,reason:'unverified-home',error:{code:e.code || 'E_SOURCE',message:e.message}};
54
54
  }
55
55
  }
56
- export function workingDocuments(source,status=loadStatus(source)) {
57
- const before=liveHome(source,status),observedAt=new Date().toISOString();
58
- const unavailable=state=>({liveMemory:{available:false,reason:state.reason,observedAt,...(state.error?{error:state.error}:{})},documents:[]});
59
- if(!before.available) return unavailable(before);
56
+ /** The home's working memory (STATE.md, log.md, notes/*.md), each capped. */
57
+ function homeDocuments(home) {
60
58
  const documents=[];
61
59
  const doc=(label,file)=>{
62
60
  const content=regular(file,DOCUMENT_BYTES);
@@ -64,9 +62,9 @@ export function workingDocuments(source,status=loadStatus(source)) {
64
62
  };
65
63
  let error;
66
64
  try {
67
- doc('Working state (STATE.md)',join(source.home,'STATE.md'));
68
- doc('Log (log.md)',join(source.home,'log.md'));
69
- const notes=safePath(join(source.home,'notes'));
65
+ doc('Working state (STATE.md)',join(home,'STATE.md'));
66
+ doc('Log (log.md)',join(home,'log.md'));
67
+ const notes=safePath(join(home,'notes'));
70
68
  function walk(dir,prefix='') {
71
69
  let entries;try {entries=fs.readdirSync(dir,{withFileTypes:true});} catch(e) {if(e.code==='ENOENT') return;throw e;}
72
70
  for(const entry of entries.sort((a,b)=>a.name<b.name?-1:a.name>b.name?1:0)) {
@@ -79,6 +77,13 @@ export function workingDocuments(source,status=loadStatus(source)) {
79
77
  }
80
78
  walk(notes);
81
79
  } catch(e) {error=e;}
80
+ return {documents,error};
81
+ }
82
+ export function workingDocuments(source,status=loadStatus(source)) {
83
+ const before=liveHome(source,status),observedAt=new Date().toISOString();
84
+ const unavailable=state=>({liveMemory:{available:false,reason:state.reason,observedAt,...(state.error?{error:state.error}:{})},documents:[]});
85
+ if(!before.available) return unavailable(before);
86
+ const {documents,error}=homeDocuments(source.home);
82
87
  const after=liveHome(source,loadStatus(source));
83
88
  if(!after.available) return unavailable(after);
84
89
  if(before.dev!==after.dev || before.ino!==after.ino) return unavailable({reason:'home-changed'});
@@ -117,3 +122,17 @@ export function inspect(source) {
117
122
  acceptedView:source.acceptedView,legacyLocalView:legacy,status,scheduler:health,liveMemory:working.liveMemory,documents
118
123
  };
119
124
  }
125
+ /** okf 4.0.3: inspect a harvest-off home (consultSource): its declaration, the
126
+ * bases it reads, and its own working memory. There is no source, custody or
127
+ * schedule to report; `oats okf bases` shows the accepted state it reads. */
128
+ export function inspectConsultOnly(source) {
129
+ const {documents,error}=homeDocuments(source.home),observedAt=new Date().toISOString();
130
+ if(error) fail(error.code==='E_PATH'?'E_PATH':'E_INSPECT_FAILED',error.message);
131
+ const path=join(source.home,'knowledge');let legacy=null;
132
+ try {const stat=fs.lstatSync(path);legacy={status:'legacy-local-view',path,ignored:true,...(stat.isSymbolicLink()?{symlink:true}:{}),note:'okf 3.0.0 ignores this okf 2.x snapshot and reads knowledge remotely; it is safe to delete by hand'};} catch(e) {if(!missing(e)) throw e;}
133
+ return {
134
+ summary:`OKF harvest off for this instance (${source.harvest.reason}): no source is registered and nothing is captured; ${documents.length} working-memory documents${legacy?'; legacy-local-view ./knowledge/ (ignored, safe to delete)':''}`,
135
+ source:null,harvest:source.harvest,owns:source.decl.owns,reads:source.decl.reads,bases:source.bindings.bases,
136
+ acceptedView:null,legacyLocalView:legacy,status:null,scheduler:null,liveMemory:{available:true,reason:'live',observedAt},documents
137
+ };
138
+ }
@@ -108,14 +108,28 @@ function finishRegistration(source) {
108
108
  scheduleSource(source);return source;
109
109
  }
110
110
  /** Harvest off (okf 4.0.0): no source, no custody, no schedule. The home keeps
111
- * a small record so retire knows there is nothing to capture. */
111
+ * a small record so retire knows there is nothing to capture, and (4.0.3) the
112
+ * soul's declaration, so consultation works without a source. */
112
113
  function harvestOff(home,sw,extra={}) {
113
- const record={version:1,harvest:'off',reason:sw.reason,rows:sw.rows,warnings:sw.warnings,at:new Date().toISOString()};
114
+ const record={version:1,harvest:'off',reason:sw.reason,rows:sw.rows,warnings:sw.warnings,...(extra.decl?{decl:extra.decl}:{}),at:new Date().toISOString()};
114
115
  atomic(instanceRecordPath(home),JSON.stringify(record,null,2)+'\n');
115
116
  ensureInstanceKnowledge(home);
116
117
  return {harvestOff:true,switch:sw,home,...extra};
117
118
  }
118
119
  export const harvestOffRecord = home => fs.existsSync(instanceRecordPath(home))?readJSON(instanceRecordPath(home)):null;
120
+ /** okf 4.0.3: what a harvest-off home consults. Consultation never depends on
121
+ * the harvest switch: such a home has no registered source by design, so it
122
+ * reads its soul's declaration (recorded at spawn; the kernel's OATS_SOUL for
123
+ * a home spawned before 4.0.3) and the deployment's bindings as they are now.
124
+ * Nothing is registered, captured or scheduled. */
125
+ export function consultSource(home) {
126
+ home=safePath(home);
127
+ const record=harvestOffRecord(home);
128
+ if(!record) fail('E_SOURCE',`no okf source for ${home}: it was not spawned with oats.okf as its knowledge capability`);
129
+ const decl=record.decl || (process.env.OATS_SOUL?declaration(fs.realpathSync(process.env.OATS_SOUL)):fail('E_OATS_SOUL_MISSING','OATS_SOUL is not set; oats.okf commands run only under the OATS kernel'));
130
+ const work=fs.existsSync(join(home,'work'))?fs.realpathSync(join(home,'work')):join(home,'work');
131
+ return {home,work,decl,bindings:loadBindings(undefined,{sourceHome:home,sourceWork:work}),harvest:{status:'off',reason:record.reason,at:record.at}};
132
+ }
119
133
  /** The source's tasks provider (its instance.json tasks-layer capability), or null. */
120
134
  function tasksProvider(meta) {
121
135
  const row=Array.isArray(meta?.capabilities)?meta.capabilities.find(c=>c && c.layer==='tasks' && typeof c.id==='string'):null;
@@ -37,7 +37,7 @@ export function completionCommand(source,id,judgmentFile) {return command(source
37
37
  // It homes in agents/oats-okf--knowledge-harvester/. Its instances get an
38
38
  // exact --name okf-harvester-<run> (50 characters): a derived
39
39
  // <agent>-<purpose> name would exceed the kernel's 64-character cap.
40
- export const HARVESTER_SOUL='oats.okf/knowledge-harvester',HARVESTER_AGENT='oats-okf--knowledge-harvester',HARVESTER_TEAM='okf';
40
+ export const HARVESTER_SOUL='oats.okf/knowledge-harvester',HARVESTER_AGENT='oats-okf--knowledge-harvester';
41
41
  export const harvesterInstance=id=>`okf-harvester-${id}`;
42
42
  /** The harvester's own completion and status commands (oats.okf-harvest). The
43
43
  * completion wrapper runs this source's frozen `oats okf complete` from the
@@ -47,15 +47,6 @@ export function harvesterCommands(source,id) {
47
47
  return {complete:['oats','okf-harvest','complete',...tail,'--judgment','<absolute-judgment.json>'].map(quote).join(' '),
48
48
  status:['oats','okf-harvest','harvest-status',...tail].map(quote).join(' ')};
49
49
  }
50
- /** The messaging capability the harvester soul resolves (from spawn --preview),
51
- * so it can join the okf team; null when it resolves none. */
52
- function harvesterMessaging(source) {
53
- try {
54
- const preview=oats(['spawn',HARVESTER_SOUL,'--dir',source.context,'--preview','--json'],source.context,{timeout:90000});
55
- const row=(Array.isArray(preview?.modules)?preview.modules:[]).find(m=>m?.layer==='messaging' && typeof m.name==='string');
56
- return {messaging:row?row.name:null};
57
- } catch(e) {return {messaging:null,error:`${e.code || 'E_RUNTIME'}: ${e.message}`};}
58
- }
59
50
  export function runSource(source,{noLaunch=false,manual=false,capturedInvocation,nativeRequest}={}) {
60
51
  const plan=capturedSource(source)?qualifyCapturedWorker(source,{context:capturedInvocation,nativeRequest}):null;
61
52
  if(!plan) requireQualifiedHelper(source);
@@ -117,7 +108,7 @@ function spawnWorker(source,run,{parent=false}={}) {
117
108
  task=`Process only durable OKF run ${id}. Load the knowledge-harvest skill first.${recovery}\n\n${evidence}\n\nWrite ./work/judgment.json per the skill, then execute the completion command below, replacing only the quoted placeholder with the absolute judgment file path (shell-quote it). A successful command, not this task, is the delivery receipt. On failure retain the worker and report it; do not self-retire. On success report receipt then retire normally.\n\n${complete}\n`;
118
109
  } else {
119
110
  const cmd=harvesterCommands(source,id);
120
- task=`Process only durable OKF run ${id}. Load the knowledge-harvest skill first.${recovery}\n\n${evidence}\n\nWrite ./work/judgment.json per the skill, then run the completion command below, replacing only the quoted placeholder with the absolute judgment file path (shell-quote it). It runs this source's frozen completion in the source deployment. A successful command, not this task, is the delivery receipt. On failure keep your home and report it; do not retire.\n\nAfter a successful completion, stay alive in the okf team until your PR is merged or closed. On every wake run the status command first, and retire only when it says retire or max-age. Never close the PR yourself.\n\nComplete: ${cmd.complete}\nStatus: ${cmd.status}\n`;
111
+ task=`Process only durable OKF run ${id}. Load the knowledge-harvest skill first.${recovery}\n\n${evidence}\n\nWrite ./work/judgment.json per the skill, then run the completion command below, replacing only the quoted placeholder with the absolute judgment file path (shell-quote it). It runs this source's frozen completion in the source deployment. A successful command, not this task, is the delivery receipt. On failure keep your home and report it; do not retire.\n\nAfter a successful completion, stay alive until your PR is merged or closed. On every wake run the status command first, and retire only when it says retire or max-age. Never close the PR yourself.\n\nComplete: ${cmd.complete}\nStatus: ${cmd.status}\n`;
121
112
  }
122
113
  const taskFile=join(dirname(runPath(source,id)),'TASK.md');
123
114
  const actualTask=run.capturedWorker?task.replace('On success report receipt then retire normally.',`On success report the actual receipt and include run ${id} in your final assistant reply. RETAIN this home/history. Public captured retirement is not qualified; never use legacy retirement or self-retire.`) :task;
@@ -143,11 +134,9 @@ function spawnWorker(source,run,{parent=false}={}) {
143
134
  const args=['spawn',HARVESTER_SOUL,'--name',harvesterInstance(id),'--dir',source.context,harnessFlag(source),source.execution.runtime,'--no-launch','--task-file',taskFile,'--json'];
144
135
  if(source.execution.model) args.push('--model',source.execution.model);
145
136
  if(parent) args.push('--parent',source.instance);
146
- // Join the okf team through the soul's messaging capability, as a trigger
147
- // spawn does; without one the harvester cannot talk to the maintainer.
148
- const team=harvesterMessaging(source);
149
- if(team.messaging) args.push('--provider',team.messaging,`join=${HARVESTER_TEAM}`);
150
- run.team={team:HARVESTER_TEAM,messaging:team.messaging,...(team.error?{error:team.error}:{})};
137
+ // okf 4.0.2: no team join. The harvester lives in the deployment's default
138
+ // team, where the maintainer reaches it; a deployment that wants it in
139
+ // another team opts in locally, as for any soul.
151
140
  try {
152
141
  run.worker=oats(args,source.context,{timeout:90000});
153
142
  if(!run.worker.instance || !run.worker.home) fail('E_RUNTIME','spawn receipt lacks worker identity');
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "capability": "oats.okf",
3
3
  "command": "okf",
4
- "version": "4.0.1",
4
+ "version": "4.0.4",
5
5
  "compatibility": {
6
6
  "oats": ">=0.29.0"
7
7
  },
@@ -82,14 +82,17 @@
82
82
  "setting harvest-runtime must be pi, claude or codex",
83
83
  "setting harvest-model must be null or a non-empty string",
84
84
  "check action is not an admitted knowledge operation",
85
- "bound runtime bindings file is missing or invalid",
85
+ "OKF settings missing bindings-file: set settings.oats.okf bindings-file, or deactivate oats.okf",
86
+ "OKF soul declaration missing: create okf.json for this soul, or deactivate oats.okf",
87
+ "OKF binding missing or invalid: provision soul OKF declaration and bindings file, or deactivate oats.okf",
88
+ "OKF bindings file missing or invalid: repair configured bindings file, or deactivate oats.okf",
86
89
  "more than 64 git knowledge bases declared",
87
90
  "declared knowledge base could not be staged from its git source",
88
91
  "declared knowledge base is not a validated knowledge tree",
89
92
  "knowledge base owner or remote custody requirement not met",
90
93
  "staged git source does not match the declared knowledge base",
91
94
  "harvest runtime command is not installed on this host",
92
- "harvest runtime could not be qualified against the accepted bases"
95
+ "harvest runtime is not qualified: install/configure the selected harvest runtime or set harvest off"
93
96
  ]
94
97
  },
95
98
  "inject": "injects/okf.md",
@@ -74,7 +74,7 @@ export function complete(flags, env = process.env) {
74
74
  let answer; try { answer = JSON.parse(r.stdout); } catch { /* below */ }
75
75
  if (answer?.schemaVersion === 1 && answer.ok === true) return { deployment: source.context, ...answer.result };
76
76
  const code = answer?.error?.code || 'E_COMPLETE', message = answer?.error?.message || (r.error?.message || r.stderr || `exit ${r.status}`).trim();
77
- if (INACTIVE.has(code)) fail('E_SOURCE_INACTIVE', `oats.okf cannot run for source soul ${source.agent} in ${source.context} (${code}: ${message}). Nothing was published: report this to the okf team and your operator, and stay.`, { cause: code });
77
+ if (INACTIVE.has(code)) fail('E_SOURCE_INACTIVE', `oats.okf cannot run for source soul ${source.agent} in ${source.context} (${code}: ${message}). Nothing was published: report this to your operator, and stay.`, { cause: code });
78
78
  fail(code, `${message} (completion ran in ${source.context}; keep your home and report)`);
79
79
  }
80
80
  /** "7d" | "48h" | "90m" | seconds → milliseconds. */
@@ -112,7 +112,7 @@ export function harvestStatus(flags, env = process.env, { now = Date.now(), view
112
112
  if (!judged || pending.length) { action = age >= limit ? 'max-age' : 'stay'; reason = !judged ? 'the run is not completed yet' : `destinations not delivered: ${pending.map((d) => d.alias).join(', ')}`; }
113
113
  else if (open.length) { action = age >= limit ? 'max-age' : 'stay'; reason = `open PR: ${open.map((d) => d.pr.url).join(', ')}`; }
114
114
  else { action = 'retire'; reason = destinations.some((d) => d.pr) ? 'every PR is merged or closed' : 'no PR was needed (no-change or directory publication)'; }
115
- if (action === 'max-age') reason += `; older than harvester-max-age (${Math.round(limit / 3600000)}h): tell the okf team and retire, never close the PR`;
115
+ if (action === 'max-age') reason += `; older than harvester-max-age (${Math.round(limit / 3600000)}h): tell your operator and retire, never close the PR`;
116
116
  return { run: run.id, status: run.status, ageSeconds: Math.round(age / 1000), maxAgeSeconds: limit / 1000, destinations, action, reason };
117
117
  }
118
118
  function text(event, r) {
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "capability": "oats.okf-harvest",
3
3
  "command": "okf-harvest",
4
- "version": "4.0.1",
4
+ "version": "4.0.4",
5
5
  "compatibility": {
6
6
  "oats": ">=0.29.0"
7
7
  },
8
- "description": "The OKF knowledge harvester: judges one source instance's captured notes and session transcript by the OKF promotion doctrine, opens the labelled harvest PR with its provenance block, and stays in the okf team until the PR is merged or closed.",
8
+ "description": "The OKF knowledge harvester: judges one source instance's captured notes and session transcript by the OKF promotion doctrine, opens the labelled harvest PR with its provenance block, and stays alive until the PR is merged or closed.",
9
9
  "requires": [
10
10
  { "command": "gh", "why": "read the harvest PR's state (harvest-status)" }
11
11
  ],
12
12
  "settings": {
13
13
  "harvester-max-age": {
14
14
  "default": "7d",
15
- "description": "How long a harvester stays alive waiting for its PR (a duration like 7d, 48h or 90m, or seconds). At max-age it tells the okf team and retires; it never closes the PR."
15
+ "description": "How long a harvester stays alive waiting for its PR (a duration like 7d, 48h or 90m, or seconds). At max-age it tells its operator and retires; it never closes the PR."
16
16
  }
17
17
  },
18
18
  "skills": [
@@ -5,8 +5,8 @@ description: >-
5
5
  durable run's input fully (notes AND the captured transcript windows), cite
6
6
  the turn ids relied on, extract task references, judge with knowledge-theory,
7
7
  stage edits on the owned nodes, complete with `oats okf-harvest complete`
8
- (which opens the labelled PR with its provenance block), then stay alive in
9
- the okf team until the PR is merged or closed. Use when TASK.md names an OKF
8
+ (which opens the labelled PR with its provenance block), then stay alive
9
+ until the PR is merged or closed. Use when TASK.md names an OKF
10
10
  run, when a maintainer messages about your harvest PR, on every wake while
11
11
  your PR is open, and for operator-requested rejudgment.
12
12
  ---
@@ -126,13 +126,13 @@ publishes:
126
126
 
127
127
  A failed or uncertain completion is NOT success. Keep your home and work,
128
128
  report the recovery need, and stay. If it reports that the source's oats.okf
129
- is not active or not trusted in its deployment, report exactly that to the
130
- okf team and your operator, and stay: nothing was published. Never run
129
+ is not active or not trusted in its deployment, report exactly that to
130
+ your operator, and stay: nothing was published. Never run
131
131
  `git push` or `gh pr create` by hand; never rerun a failed delivery by hand.
132
132
 
133
133
  ## 6. Stay alive until the PR is merged or closed
134
134
 
135
- After a PR opens you stay **alive and idle** in the okf team: the maintainer
135
+ After a PR opens you stay **alive and idle**: the maintainer
136
136
  may ask about your judgment.
137
137
 
138
138
  - **On every wake** (a message, a human, a resumed session), first run
@@ -142,7 +142,7 @@ may ask about your judgment.
142
142
  - `retire`: every PR is merged or closed, or the run needed none (no-change,
143
143
  directory publication). Report the outcome, then retire (the oats skill);
144
144
  - `max-age`: the run is older than `harvester-max-age` (default 7 days).
145
- Tell the okf team the PR is still open and that you are retiring, then
145
+ Tell your operator the PR is still open and that you are retiring, then
146
146
  retire. **Never close the PR yourself.**
147
147
  - **Messages** (C4, subject prefix `okf:` plus the PR URL):
148
148
  - `okf: question <PR>`: answer from your judgment and the evidence, citing
@@ -137,10 +137,10 @@ export function notifyHarvester(flags, env = process.env, { view = viewPr } = {}
137
137
  'amend-request': `An amendment request on your harvest PR ${pr.url}: see the okf-review comment and reply with the change you would make.`,
138
138
  amended: `I amended your harvest PR ${pr.url}; see the okf-review comment.`,
139
139
  }[flags.state];
140
- return { to: h.alias || h.instance, instance: h.instance, alias: h.alias, team: 'okf', subject: `okf: ${flags.state} ${pr.url}`, body, send: 'send this with your messaging capability in the okf team' };
140
+ return { to: h.alias || h.instance, instance: h.instance, alias: h.alias, subject: `okf: ${flags.state} ${pr.url}`, body, send: 'send this with your messaging capability' };
141
141
  }
142
142
  function text(event, r) {
143
- if (event === 'notify-harvester') return `to: ${r.to} (team okf)\nsubject: ${r.subject}\n\n${r.body}`;
143
+ if (event === 'notify-harvester') return `to: ${r.to}\nsubject: ${r.subject}\n\n${r.body}`;
144
144
  const lines = [`${r.pr.url} ${r.pr.state}${r.pr.draft ? ' (draft)' : ''} ${r.pr.head}@${String(r.pr.headSha).slice(0, 12)} → ${r.pr.base} [${r.pr.labels.join(', ')}]`];
145
145
  lines.push(r.provenance.valid ? `provenance: run ${r.provenance.value.run}, source ${r.provenance.value.source.soul}/${r.provenance.value.source.instance}, harvester ${r.harvester.alias || r.harvester.instance}` : `provenance INVALID: ${r.provenance.problems.join('; ')}`);
146
146
  if (r.tasks) lines.push(`tasks: ${r.tasks.refs.join(', ') || '(none)'} — ${r.tasks.note}`);
@@ -9,4 +9,4 @@ first, and judge by **knowledge-theory**.
9
9
  silently. A PR that would supersede a human-accepted decision gets
10
10
  `okf-needs-human` and a human, not a merge.
11
11
  - Settle the PR (merge, amend and merge, request changes, close), notify the
12
- harvester in the okf team, then retire.
12
+ harvester, then retire.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "capability": "oats.okf-maintenance",
3
3
  "command": "okf-maintenance",
4
- "version": "4.0.1",
4
+ "version": "4.0.4",
5
5
  "compatibility": {
6
6
  "oats": ">=0.29.0"
7
7
  },
8
- "description": "The OKF knowledge maintainer: reviews one harvest PR by the OKF promotion doctrine, situates it in the base, amends and merges or closes it, never silently supersedes a human-accepted decision, and notifies the harvester in the okf team.",
8
+ "description": "The OKF knowledge maintainer: reviews one harvest PR by the OKF promotion doctrine, situates it in the base, amends and merges or closes it, never silently supersedes a human-accepted decision, and notifies the harvester.",
9
9
  "requires": [
10
10
  { "command": "gh", "why": "read, comment on, amend and merge the harvest PR" },
11
11
  { "command": "git", "why": "check out the knowledge-base PR" }
@@ -7,7 +7,7 @@ description: >-
7
7
  through your tasks capability when you can, judge by knowledge-theory, then
8
8
  merge, amend and merge, request changes from the harvester, or close — never
9
9
  superseding a human-accepted decision silently. Use when TASK.md names a
10
- knowledge-base PR, when a harvester answers you in the okf team, or when a
10
+ knowledge-base PR, when a harvester answers you, or when a
11
11
  trigger re-runs you on a PR you may already have reviewed.
12
12
  ---
13
13
 
@@ -6,7 +6,7 @@ description: >-
6
6
  (`oats-triggers/okf-harvest-review.yaml` in a member repo, `from:
7
7
  oats.okf:harvest-review`, with `runsOn` naming the one host and `owner` the
8
8
  GitHub account that can merge on the knowledge-base repo), or locally with
9
- `oats trigger add` for a machine-private setup. Covers the okf team, the
9
+ `oats trigger add` for a machine-private setup. Covers messaging, the
10
10
  self-approval limit and `oats trigger test`. Use when setting up knowledge
11
11
  operations, when harvest PRs are not being reviewed, when moving the
12
12
  reviewer to another host, or when asked whether a host may run the
@@ -17,7 +17,7 @@ description: >-
17
17
 
18
18
  The trigger makes a harvest PR get reviewed: when a PR labelled `okf-harvest`
19
19
  opens on the knowledge-base (KB) repository, the host tick spawns a new
20
- `oats.okf/knowledge-maintainer` in the `okf` team to review it. It runs on one
20
+ `oats.okf/knowledge-maintainer` to review it. It runs on one
21
21
  machine, acting as one GitHub account, and that account must be able to
22
22
  **merge** on the KB repository.
23
23
 
@@ -43,28 +43,16 @@ the reviewer's account are the same GitHub account, then either:
43
43
 
44
44
  Say which one applies when you report the setup.
45
45
 
46
- ## 3. The okf team
46
+ ## 3. Messaging
47
47
 
48
- The package souls carry `team: okf`. The workspace must declare it, with its
49
- messaging mapping. Messaging is aweb (`oats.aweb`, the workspace default);
50
- it needs oats.aweb 1.15.0 or later, which honours the `join=okf` the
51
- harvester spawn and the trigger's `teams: [okf]` pass:
48
+ The harvester and the maintainer message each other through the soul's
49
+ messaging capability (`oats.aweb`, the workspace default). Both live in the
50
+ deployment's default team, like every instance: there is no okf team to
51
+ declare or map. A deployment that wants them in another team opts them in
52
+ locally, as for any soul.
52
53
 
53
- ```yaml
54
- # oats-workspace.yaml
55
- teams:
56
- okf: { description: Knowledge operations }
57
- defaults:
58
- messaging: { oats.aweb: { from: package } }
59
- messaging:
60
- byTeam:
61
- okf: { team: aweb:<your-org>.okf }
62
- ```
63
-
64
- Another messaging provider works the same way if it honours `join`.
65
-
66
- Without it, the souls list with `E_TEAM_UNKNOWN`, and the harvester and the
67
- maintainer cannot message each other.
54
+ Without a messaging capability they cannot message each other: the
55
+ maintainer's notices and questions do not reach the harvester.
68
56
 
69
57
  ## 4. Declare it in the workspace (the default)
70
58
 
@@ -126,7 +114,7 @@ oats trigger status
126
114
 
127
115
  - `oats trigger test` checks gh auth and where its credential comes from, the
128
116
  repository and your merge permissions, the soul, its messaging capability,
129
- the okf team, the host/owner match, and what would fire now. It spawns
117
+ the host/owner match, and what would fire now. It spawns
130
118
  nothing. It must pass before you report the setup done; fix what it names.
131
119
  - **Credentials reach the tick through the host timer, not your shell.** A
132
120
  `GH_TOKEN` exported in your shell does not reach it; `gh auth login` with the
@@ -0,0 +1,26 @@
1
+ ## Working on the OATS framework repository (this workspace's experts)
2
+
3
+ This is the OATS-repo-specific part of the expert role. It lives in the oats repo (a private
4
+ capability, `oats.workspace-experts`, assigned to this workspace's expert souls), not in
5
+ the generic engineering package.
6
+
7
+ **Surfaces and their developers.** To drive development, launch the developer that owns
8
+ the surface; for work across surfaces, one developer per surface.
9
+
10
+ | Surface | Paths | Developer soul | Expert |
11
+ |---|---|---|---|
12
+ | Kernel & CLI | `lib/`, `bin/`, `docs/*.schema.json` | `oats-kernel-developer` | `oats-kernel-expert` |
13
+ | Desktop app & server | `packages/desktop/` (not views) | `oats-desktop-developer` | `oats-desktop-expert` |
14
+ | Desktop design | `packages/desktop/renderer/` views, styles, copy | `oats-desktop-designer` | `oats-desktop-expert` |
15
+ | Provider packages | `oats-aweb`, `oats-okf`, and the other package repos | `oats-integrations-developer` | `integrations-expert` (and the package's own expert) |
16
+ | Docs & skills | `docs/`, `oats-package/`, `skills/` | the developer of the surface they document | the owning expert |
17
+
18
+ **Your own worktrees.** You may drive a piece of work in your own session instead of
19
+ launching a developer, when that's the better call (a small cross-cutting change, a spike,
20
+ a release PR). Use your work-mode briefing's extra worktrees, named for the surface
21
+ (`.work-kernel`, `.work-desktop`, `.work-docs`), with the developer's discipline, including
22
+ the adversarial review loop. Launching a developer is still the default.
23
+
24
+ **Delivery.** Every change reaches main by a PR. The owning expert lands it (opens it,
25
+ answers reviews, gets it merged). In this repository, merges are done by the maintainer
26
+ (`oats-expert`), so getting its approval is part of landing.
@@ -0,0 +1,9 @@
1
+ {
2
+ "capability": "oats.workspace-experts",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "compatibility": { "oats": ">=0.29.0" },
6
+ "description": "The OATS framework repository's part of the expert role: which developer soul owns each surface, when an expert drives work in its own worktrees, and how changes land here (PRs merged by the maintainer, oats-expert). Assigned to this workspace's expert souls, beside oats.engineering-expert.",
7
+ "requires": [],
8
+ "inject": "injects/oats-experts.md"
9
+ }