@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.
- package/README.md +12 -6
- package/bin/oats.mjs +194 -50
- package/capabilities/oats-aweb/bin/oats-aweb.mjs +538 -204
- package/capabilities/oats-aweb/injects/aweb.md +1 -1
- package/capabilities/oats-aweb/lib/binding-wire.mjs +31 -22
- package/capabilities/oats-aweb/oats.json +5 -12
- package/capabilities/oats-aweb/skills/VENDORED.md +4 -4
- package/capabilities/oats-aweb/skills/aweb-team-membership/SKILL.md +1 -1
- package/capabilities/oats-aweb/skills/oats-aweb/SKILL.md +83 -13
- package/capabilities/oats-code-review/injects/reviewer.md +26 -0
- package/capabilities/oats-code-review/oats.json +16 -0
- package/capabilities/oats-code-review/skills/adversarial-review/SKILL.md +66 -0
- package/capabilities/oats-code-review/skills/review-dev-docs/SKILL.md +30 -0
- package/capabilities/oats-code-review/skills/security-review/SKILL.md +56 -0
- package/capabilities/oats-code-review/skills/simplification-review/SKILL.md +34 -0
- package/capabilities/oats-developer/injects/developer.md +38 -0
- package/capabilities/oats-developer/oats.json +17 -0
- package/capabilities/oats-developer/skills/execution-strategy/SKILL.md +43 -0
- package/capabilities/oats-developer/skills/maintain-dev-docs/SKILL.md +47 -0
- package/capabilities/oats-developer/skills/run-the-review-loop/SKILL.md +65 -0
- package/capabilities/oats-developer/skills/understand-the-spec/SKILL.md +37 -0
- package/capabilities/oats-developer/skills/worktrees/SKILL.md +36 -0
- package/capabilities/oats-engineering-expert/injects/expert.md +37 -0
- package/capabilities/oats-engineering-expert/oats.json +17 -0
- package/capabilities/oats-engineering-expert/skills/coordinate-developers/SKILL.md +37 -0
- package/capabilities/oats-engineering-expert/skills/coordinate-experts/SKILL.md +52 -0
- package/capabilities/oats-engineering-expert/skills/land-your-prs/SKILL.md +50 -0
- package/capabilities/oats-engineering-expert/skills/plan-and-spec/SKILL.md +53 -0
- package/capabilities/oats-engineering-expert/skills/verify-developer-work/SKILL.md +49 -0
- package/capabilities/oats-okf/bin/oats-okf.mjs +8 -4
- package/capabilities/oats-okf/lib/binding-wire.mjs +47 -15
- package/capabilities/oats-okf/lib/inspection.mjs +26 -7
- package/capabilities/oats-okf/lib/sources.mjs +16 -2
- package/capabilities/oats-okf/lib/worker.mjs +5 -16
- package/capabilities/oats-okf/oats.json +6 -3
- package/capabilities/oats-okf-harvest/bin/okf-harvest.mjs +2 -2
- package/capabilities/oats-okf-harvest/oats.json +3 -3
- package/capabilities/oats-okf-harvest/skills/knowledge-harvest/SKILL.md +6 -6
- package/capabilities/oats-okf-maintenance/bin/okf-maintenance.mjs +2 -2
- package/capabilities/oats-okf-maintenance/injects/maintainer.md +1 -1
- package/capabilities/oats-okf-maintenance/oats.json +2 -2
- package/capabilities/oats-okf-maintenance/skills/knowledge-review/SKILL.md +1 -1
- package/capabilities/oats-okf-maintenance/skills/okf-trigger-setup/SKILL.md +11 -23
- package/capabilities/oats-workspace-experts/injects/oats-experts.md +26 -0
- package/capabilities/oats-workspace-experts/oats.json +9 -0
- package/docs/capabilities.md +160 -171
- package/docs/capability-manifest.schema.json +6 -11
- package/docs/configuration.md +213 -64
- package/docs/design/2026-09-16-knowledge-capability-contract.md +36 -50
- package/docs/design/2026-09-23-workspace-module-contracts.md +377 -544
- package/docs/design/2026-09-26-okf-knowledge-operations.md +132 -357
- package/docs/design/2026-09-27-team-model-v2.md +97 -117
- package/docs/design/2026-09-28-automations-trust.md +38 -0
- package/docs/design/2026-09-28-soul-launch-preference.md +63 -0
- package/docs/design/HISTORY.md +65 -0
- package/docs/design/README.md +23 -54
- package/docs/desktop-cli-api.md +1787 -1777
- package/docs/desktop.md +30 -91
- package/docs/execution-targets.md +146 -292
- package/docs/first-team.md +31 -17
- package/docs/implementation.md +76 -288
- package/docs/integrations.md +118 -320
- package/docs/knowledge-capability-authoring.md +25 -52
- package/docs/knowledge-reference/acceptance.md +3 -3
- package/docs/knowledge-reference/adoption.md +1 -1
- package/docs/knowledge-reference/harvester.md +2 -2
- package/docs/knowledge-reference/package-craft.md +3 -3
- package/docs/knowledge-reference/provider-mapping.md +3 -6
- package/docs/knowledge-reference/reader-capture.md +3 -3
- package/docs/knowledge-theory.md +62 -166
- package/docs/knowledge.md +225 -404
- package/docs/layers.md +42 -97
- package/docs/oats-local.schema.json +58 -5
- package/docs/oats-membership.schema.json +1 -8
- package/docs/oats-package.schema.json +5 -5
- package/docs/oats-workspace.schema.json +8 -22
- package/docs/official-catalog.md +25 -28
- package/docs/packages.md +45 -63
- package/docs/plans/0.30-close-out.md +61 -0
- package/docs/release-lane.md +77 -0
- package/docs/release-notes/oats-framework-v1.1.3.md +10 -8
- package/docs/release-notes/v0.19.0.md +48 -147
- package/docs/release-notes/v0.19.1.md +2 -3
- package/docs/release-notes/v0.19.3.md +2 -15
- package/docs/release-notes/v0.20.0.md +0 -15
- package/docs/release-notes/v0.22.0.md +71 -138
- package/docs/release-notes/v0.22.1.md +42 -90
- package/docs/release-notes/v0.22.10.md +1 -1
- package/docs/release-notes/v0.22.11.md +1 -47
- package/docs/release-notes/v0.22.12.md +4 -13
- package/docs/release-notes/v0.22.13.md +1 -42
- package/docs/release-notes/v0.22.14.md +3 -11
- package/docs/release-notes/v0.22.15.md +1 -46
- package/docs/release-notes/v0.22.16.md +6 -8
- package/docs/release-notes/v0.22.18.md +1 -99
- package/docs/release-notes/v0.22.19.md +3 -14
- package/docs/release-notes/v0.22.2.md +6 -15
- package/docs/release-notes/v0.22.3.md +0 -1
- package/docs/release-notes/v0.22.4.md +1 -14
- package/docs/release-notes/v0.22.5.md +2 -12
- package/docs/release-notes/v0.22.6.md +0 -3
- package/docs/release-notes/v0.23.0.md +9 -25
- package/docs/release-notes/v0.23.1.md +9 -25
- package/docs/release-notes/v0.23.2.md +2 -4
- package/docs/release-notes/v0.24.0.md +56 -97
- package/docs/release-notes/v0.24.1.md +7 -11
- package/docs/release-notes/v0.24.10.md +34 -45
- package/docs/release-notes/v0.24.11.md +12 -20
- package/docs/release-notes/v0.24.12.md +35 -48
- package/docs/release-notes/v0.24.13.md +34 -41
- package/docs/release-notes/v0.24.2.md +9 -13
- package/docs/release-notes/v0.24.3.md +7 -11
- package/docs/release-notes/v0.24.4.md +6 -6
- package/docs/release-notes/v0.24.5.md +6 -10
- package/docs/release-notes/v0.24.6.md +2 -5
- package/docs/release-notes/v0.24.7.md +46 -75
- package/docs/release-notes/v0.24.8.md +58 -96
- package/docs/release-notes/v0.24.9.md +38 -54
- package/docs/release-notes/v0.25.0.md +59 -76
- package/docs/release-notes/v0.25.1.md +57 -81
- package/docs/release-notes/v0.25.2.md +51 -70
- package/docs/release-notes/v0.25.3.md +11 -13
- package/docs/release-notes/v0.25.4.md +9 -13
- package/docs/release-notes/v0.25.5.md +3 -5
- package/docs/release-notes/v0.25.6.md +20 -29
- package/docs/release-notes/v0.25.7.md +5 -7
- package/docs/release-notes/v0.25.8.md +26 -39
- package/docs/release-notes/v0.26.0.md +175 -646
- package/docs/release-notes/v0.27.0.md +4 -5
- package/docs/release-notes/v0.27.1.md +4 -6
- package/docs/release-notes/v0.27.2.md +1 -1
- package/docs/release-notes/v0.28.0.md +57 -124
- package/docs/release-notes/v0.29.0.md +89 -208
- package/docs/release-notes/v0.29.1.md +1 -1
- package/docs/release-notes/v0.29.2.md +3 -4
- package/docs/release-notes/v0.30.0.md +205 -0
- package/docs/schedules.md +280 -363
- package/docs/servers.md +99 -117
- package/docs/soul.schema.json +2 -9
- package/docs/souls-and-instances.md +145 -158
- package/docs/workspaces.md +132 -215
- package/lib/automations.mjs +21 -6
- package/lib/core.mjs +226 -74
- package/lib/instance-events.mjs +1 -1
- package/lib/instance-inspect.mjs +109 -34
- package/lib/instance-lifecycle.mjs +14 -1
- package/lib/instance-resolution.mjs +26 -27
- package/lib/launch-preference.mjs +87 -0
- package/lib/materialize.mjs +3 -3
- package/lib/resolve.mjs +29 -87
- package/lib/schedule.mjs +1 -1
- package/lib/teams-verbs.mjs +195 -0
- package/lib/teams.mjs +190 -0
- package/lib/triggers.mjs +2 -2
- package/lib/workspace.mjs +54 -147
- package/package-catalog.json +9 -15
- package/package.json +1 -1
- package/skills/oats-getting-started/SKILL.md +25 -13
- package/capabilities/oats-review/injects/review.md +0 -69
- package/capabilities/oats-review/oats.json +0 -10
- package/capabilities/oats-review/skills/code-review/SKILL.md +0 -44
- package/capabilities/oats-review/skills/security-review/SKILL.md +0 -59
- package/docs/conventions.md +0 -90
- package/docs/design/2026-09-07-architecture-reassessment.md +0 -131
- package/docs/design/2026-09-07-desktop-souls-capabilities.md +0 -50
- package/docs/design/2026-09-07-mobile-agent-management-proposal.md +0 -228
- package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +0 -558
- package/docs/design/2026-09-13-knowledge-and-memory-direction.md +0 -744
- package/docs/design/2026-09-13-knowledge-implementation.md +0 -127
- package/docs/design/2026-09-13-knowledge-location-contract.md +0 -340
- package/docs/design/2026-09-14-artifact-retention-contract.md +0 -190
- package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +0 -708
- package/docs/design/2026-09-14-portable-souls-contract-amendments.md +0 -85
- package/docs/design/2026-09-14-portable-souls-explainer.md +0 -750
- package/docs/design/2026-09-15-captured-dispatch.md +0 -127
- package/docs/design/2026-09-15-captured-resolution-records.md +0 -143
- package/docs/design/2026-09-15-package-preparation.md +0 -100
- package/docs/design/2026-09-15-portable-data-contract.md +0 -121
- package/docs/design/2026-09-15-portable-declarations.md +0 -189
- package/docs/design/2026-09-15-portable-souls-handoff.md +0 -150
- package/docs/design/2026-09-15-portable-souls-implementation.md +0 -417
- package/docs/design/2026-09-15-selection-lock-and-approval.md +0 -122
- package/docs/design/2026-09-15-source-observation.md +0 -119
- package/docs/design/2026-09-16-captured-admission.md +0 -77
- package/docs/design/2026-09-16-captured-helper-dispatch.md +0 -105
- package/docs/design/2026-09-16-captured-launch-inputs.md +0 -42
- package/docs/design/2026-09-16-command-profile-preparation.md +0 -86
- package/docs/design/2026-09-16-fresh-install-first-rollout.md +0 -47
- package/docs/design/2026-09-16-fresh-operator-walkthrough.md +0 -282
- package/docs/design/2026-09-16-messaging-capability-contract.md +0 -59
- package/docs/design/2026-09-16-portable-migration-evidence.md +0 -158
- package/docs/design/2026-09-16-portable-onboarding.md +0 -179
- package/docs/design/2026-09-16-prepare-request-transport.md +0 -26
- package/docs/design/2026-09-16-provider-binding-codecs.md +0 -98
- package/docs/design/2026-09-16-provider-binding-wire.md +0 -274
- package/docs/design/2026-09-17-capability-helper-input-contract.md +0 -95
- package/docs/design/2026-09-17-captured-backend-parity.md +0 -53
- package/docs/design/2026-09-17-captured-native-start.md +0 -58
- package/docs/design/2026-09-17-portable-boundary-hookup.md +0 -19
- package/docs/design/2026-09-17-portable-boundary-resources.md +0 -52
- package/docs/design/2026-09-17-public-captured-start.md +0 -108
- package/docs/design/2026-09-17-public-prepare-request.md +0 -90
- package/docs/design/2026-09-18-captured-pi-host.md +0 -205
- package/docs/design/2026-09-18-first-cut-release-checklist.md +0 -131
- package/docs/design/2026-09-18-herdr-protocol-compatibility.md +0 -60
- package/docs/design/2026-09-20-redesign-program-board.md +0 -142
- package/docs/design/2026-09-20-workspace-and-portable-adoption-plan.md +0 -289
- package/docs/design/2026-09-20-workspace-onboarding-public.md +0 -207
- package/docs/design/2026-09-22-desktop-parity-seams.md +0 -58
- package/docs/design/2026-09-23-simplified-workspace-model.md +0 -711
- package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +0 -65
- package/docs/design/2026-09-24-desktop-phase-f-boundary.md +0 -242
- package/docs/design/2026-09-24-phase-d-plan.md +0 -305
- package/docs/design/2026-09-25-teams-contract.md +0 -258
- package/docs/design/2026-09-26-desktop-design-brief-architecture.md +0 -241
- package/docs/design/desktop-ux-plan.md +0 -362
- package/docs/design/launch-configurations.md +0 -168
- package/docs/design/okf-mirror-provenance.md +0 -105
- package/docs/design/operations-contract.md +0 -141
- package/docs/oats-member.schema.json +0 -38
- package/skills/integration-authoring/SKILL.md +0 -84
- package/skills/oats-support/SKILL.md +0 -79
- package/skills/skill-craft/SKILL.md +0 -109
- 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
|
-
'
|
|
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
|
|
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,['
|
|
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
|
-
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
const
|
|
286
|
-
|
|
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
|
-
|
|
300
|
-
|
|
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:'
|
|
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:'
|
|
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
|
-
|
|
57
|
-
|
|
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(
|
|
68
|
-
doc('Log (log.md)',join(
|
|
69
|
-
const notes=safePath(join(
|
|
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'
|
|
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
|
|
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
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
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.
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
9
|
-
|
|
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
|
|
130
|
-
|
|
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
|
|
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
|
|
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,
|
|
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}
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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.
|
|
46
|
+
## 3. Messaging
|
|
47
47
|
|
|
48
|
-
The
|
|
49
|
-
messaging
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
54
|
-
|
|
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
|
|
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
|
+
}
|