@awebai/oats 0.24.3 → 0.24.5
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/bin/oats.mjs +25 -6
- package/capabilities/oats-aweb/oats.json +44 -3
- package/capabilities/oats-okf/lib/binding-wire.mjs +57 -7
- package/capabilities/oats-okf/oats.json +12 -3
- package/docs/capability-manifest.schema.json +15 -1
- package/docs/design/2026-09-16-provider-binding-wire.md +28 -1
- package/docs/design/2026-09-20-redesign-program-board.md +60 -5
- package/docs/design/2026-09-20-workspace-onboarding-public.md +18 -1
- package/docs/first-team.md +10 -4
- package/docs/layers.md +1 -1
- package/docs/official-marketplace.md +1 -1
- package/docs/packages.md +2 -2
- package/docs/release-notes/oats-framework-v1.1.3.md +11 -0
- package/docs/release-notes/v0.24.3.md +1 -1
- package/docs/release-notes/v0.24.4.md +10 -0
- package/docs/release-notes/v0.24.5.md +12 -0
- package/docs/workspace-adoption.md +77 -45
- package/docs/workspaces.md +1 -1
- package/lib/captured-launch-request.mjs +22 -2
- package/lib/core.mjs +25 -3
- package/lib/helper-injection-policy.mjs +7 -1
- package/lib/portable-onboarding.mjs +11 -4
- package/lib/prepare-composition.mjs +21 -2
- package/lib/prepared-bindings.mjs +2 -1
- package/lib/provider-binding-broker.mjs +3 -2
- package/lib/provider-binding-wire.mjs +11 -5
- package/lib/provider-binding.mjs +7 -2
- package/lib/provider-reasons.mjs +77 -0
- package/lib/setup-expert-source.mjs +25 -1
- package/package-catalog.json +3 -3
- package/package.json +1 -1
package/bin/oats.mjs
CHANGED
|
@@ -171,7 +171,12 @@ function prepareCmd() {
|
|
|
171
171
|
// Pass the whole request to the one public validator/resolver. Unknown
|
|
172
172
|
// fields are refused there, never filtered or filled from ambient state.
|
|
173
173
|
const result = prepareCapturedComposition(input);
|
|
174
|
-
if (!result.resolution)
|
|
174
|
+
if (!result.resolution) {
|
|
175
|
+
const summary = "preparation is incomplete; no executable resolution was published";
|
|
176
|
+
const reasons = (result.problems ?? []).filter(p => p.key !== undefined || (p.slot && p.capability))
|
|
177
|
+
.map(p => `[${p.slot && p.capability ? `${p.capability}/${p.slot}` : p.code}] ${p.key === undefined ? "" : `${JSON.stringify(p.key)}: `}${p.message}`);
|
|
178
|
+
fail("needs-configuration", JSON_MODE ? summary : [summary, ...reasons].join("\n"), result);
|
|
179
|
+
}
|
|
175
180
|
if (JSON_MODE) jsonOk(result); else console.log(JSON.stringify(result, null, 2));
|
|
176
181
|
} catch (error) { fail(error.code || "E_PREPARE_FAILED", error.message); }
|
|
177
182
|
}
|
|
@@ -4323,7 +4328,14 @@ function onboardCmd() {
|
|
|
4323
4328
|
if (findAgent(root, SETUP_EXPERT)) throw Object.assign(new Error("oats-setup-expert already exists; it will not be overwritten"), { code: "E_AGENT_EXISTS" });
|
|
4324
4329
|
const edition = loadSetupExpertEdition(values.get("workspace"));
|
|
4325
4330
|
for (const key of ["description", "runtime", "model"]) if (edition.declaration[key] !== undefined) assertSafeConfigValue(edition.declaration[key], `setup edition ${key}`);
|
|
4326
|
-
const catalog = officialPackageCatalog()
|
|
4331
|
+
const catalog = officialPackageCatalog();
|
|
4332
|
+
// Catalog precedence: an explicit OATS_PACKAGE_CATALOG override, else the entry the WORKSPACE
|
|
4333
|
+
// publishes at the edition's revision, else the kernel's bundled snapshot. The bundled copy lags
|
|
4334
|
+
// every oats.framework release cut after this kernel's tag, and a second operator has no main
|
|
4335
|
+
// checkout to point an override at (0.24.5; second-operator finding).
|
|
4336
|
+
const bundledEntry = catalog["oats.framework"];
|
|
4337
|
+
const entry = process.env.OATS_PACKAGE_CATALOG ? bundledEntry : (edition.catalogEntry ?? bundledEntry);
|
|
4338
|
+
const catalogOrigin = process.env.OATS_PACKAGE_CATALOG ? "override" : edition.catalogEntry ? "workspace" : "bundled";
|
|
4327
4339
|
if (!Object.hasOwn(catalog, "oats.framework") || !entry?.url || !entry.ref
|
|
4328
4340
|
|| SETUP_CAPABILITIES.some(id => { const m = officialCapabilityPackage(id); return !m.available || m.package !== "oats.framework" || m.migratedCapability !== id; })) {
|
|
4329
4341
|
throw Object.assign(new Error("official oats.framework with core/setup aliases and a published revision is required"), { code: "needs-configuration" });
|
|
@@ -4355,10 +4367,17 @@ function onboardCmd() {
|
|
|
4355
4367
|
const text = replaceCapabilitiesBlock(before ?? `name: ${scaffoldConfigName(deployment)}\n`, caps);
|
|
4356
4368
|
mkdirSync(deployment, { recursive: true });
|
|
4357
4369
|
acquired = acquirePackage(deployment, "oats.framework", { expectPackage: "oats.framework",
|
|
4358
|
-
catalog(id, selector) {
|
|
4370
|
+
catalog(id, selector) {
|
|
4371
|
+
const selected = id === "oats.framework" ? entry : (Object.hasOwn(catalog, id) ? catalog[id] : null);
|
|
4372
|
+
return selected?.url ? { url: selected.url, ref: selector || selected.ref, path: selected.path } : undefined;
|
|
4373
|
+
},
|
|
4359
4374
|
assertCommittable(plan) {
|
|
4360
4375
|
const pkg = plan.packages.find(p => p.package === "oats.framework");
|
|
4361
|
-
if (edition.packageIntegrity && pkg?.integrity !== edition.packageIntegrity)
|
|
4376
|
+
if (edition.packageIntegrity && pkg?.integrity !== edition.packageIntegrity) {
|
|
4377
|
+
const lag = catalogOrigin === "bundled" ? " (the kernel's bundled catalog entry lags the edition's package; onboard from the workspace or point OATS_PACKAGE_CATALOG at the reviewed list)" : "";
|
|
4378
|
+
throw Object.assign(new Error(`selected edition's same-repository package differs from the official acquisition; align the reviewed source and catalog explicitly${lag}`),
|
|
4379
|
+
{ code: "integrity-drift", details: { catalogOrigin, catalogRef: entry.ref, acquiredIntegrity: pkg?.integrity ?? null, editionPackageIntegrity: edition.packageIntegrity } });
|
|
4380
|
+
}
|
|
4362
4381
|
for (const id of SETUP_CAPABILITIES) {
|
|
4363
4382
|
const cap = plan.capabilities.find(c => c.capability === id);
|
|
4364
4383
|
if (!cap || cap.package !== "oats.framework" || cap.layer || Object.values(cap.executableSurface || {}).some(value => Array.isArray(value) && value.length)) {
|
|
@@ -4389,7 +4408,7 @@ function onboardCmd() {
|
|
|
4389
4408
|
const agent = findAgent(root, SETUP_EXPERT), composition = composeInstanceAgentsMd(created.soul, deployment, SETUP_EXPERT, "directory", "local");
|
|
4390
4409
|
planInstanceResources({ resolved: composition.resolved, soulDir: created.soul, agent, contextDir: deployment, composition });
|
|
4391
4410
|
const argv = [process.execPath, CLI_BIN, "spawn", SETUP_EXPERT, "--dir", deployment, "--no-yolo", "--task", "Help me configure this deployment and adopt my workspace with explicit approvals."];
|
|
4392
|
-
const result = { mode: "classic", captured: false, deployment, agentsRoot: root, ...created, source: edition.source,
|
|
4411
|
+
const result = { mode: "classic", captured: false, deployment, agentsRoot: root, ...created, source: edition.source, catalog: { origin: catalogOrigin, ref: entry.ref },
|
|
4393
4412
|
package: { id: pkg.package, version: pkg.version, commit: pkg.commit, path: pkg.path }, lockFile: acquired.lockFile,
|
|
4394
4413
|
capabilities: [...SETUP_CAPABILITIES], launched: false, next: { argv, command: argv.map(shellQuote).join(" ") } };
|
|
4395
4414
|
if (JSON_MODE) jsonOk(result);
|
|
@@ -4406,7 +4425,7 @@ function onboardCmd() {
|
|
|
4406
4425
|
}
|
|
4407
4426
|
} catch { /* never erase another writer's change or hide a failed rollback */ }
|
|
4408
4427
|
}
|
|
4409
|
-
fail(error.code || "E_ONBOARD_FAILED", error.message, { deployment, agentsRoot: root, packageAcquired: !!acquired, soul: created?.soul, configRestored, launched: false });
|
|
4428
|
+
fail(error.code || "E_ONBOARD_FAILED", error.message, { deployment, agentsRoot: root, packageAcquired: !!acquired, soul: created?.soul, configRestored, launched: false, ...(error.details && typeof error.details === "object" ? error.details : {}) });
|
|
4410
4429
|
}
|
|
4411
4430
|
}
|
|
4412
4431
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"capability": "oats.aweb",
|
|
3
3
|
"command": "aweb",
|
|
4
|
-
"version": "1.11.
|
|
4
|
+
"version": "1.11.2",
|
|
5
5
|
"compatibility": {
|
|
6
|
-
"oats": ">=0.24.
|
|
6
|
+
"oats": ">=0.24.4"
|
|
7
7
|
},
|
|
8
8
|
"layer": "messaging",
|
|
9
9
|
"description": "Messaging layer via aweb: per-instance team identities + native aw mail/chat skills + cross-machine team roster.",
|
|
@@ -62,6 +62,10 @@
|
|
|
62
62
|
"skills/aweb-identity"
|
|
63
63
|
],
|
|
64
64
|
"inject": "injects/aweb.md",
|
|
65
|
+
"helperInjection": {
|
|
66
|
+
"version": 1,
|
|
67
|
+
"mode": "omit"
|
|
68
|
+
},
|
|
65
69
|
"commands": {
|
|
66
70
|
"roster": "bin/oats-aweb.mjs roster",
|
|
67
71
|
"setup": "bin/oats-aweb.mjs setup",
|
|
@@ -73,7 +77,44 @@
|
|
|
73
77
|
"version": 1,
|
|
74
78
|
"normalize": "binding-normalize",
|
|
75
79
|
"bind": "binding-bind",
|
|
76
|
-
"check": "binding-check"
|
|
80
|
+
"check": "binding-check",
|
|
81
|
+
"keys": [
|
|
82
|
+
"responsibleHuman",
|
|
83
|
+
"privateTeam",
|
|
84
|
+
"wider"
|
|
85
|
+
],
|
|
86
|
+
"reasons": [
|
|
87
|
+
"messaging-enabled standalone preparation needs an explicit context key",
|
|
88
|
+
"messaging binding needs one soul declaration",
|
|
89
|
+
"messaging workspace must declare private: per-human",
|
|
90
|
+
"an explicit responsible-human binding is required",
|
|
91
|
+
"an explicit wider-membership consent list is required",
|
|
92
|
+
"a selected wider-team binding is required",
|
|
93
|
+
"a selected wider alias needs an explicit workspace team mapping",
|
|
94
|
+
"multiple soul messaging declarations",
|
|
95
|
+
"adoption team aliases have conflicting mappings",
|
|
96
|
+
"messaging settings and explicit binding selections are required",
|
|
97
|
+
"messaging declarations contain incompatible requirements",
|
|
98
|
+
"messaging input must match the supported binding contract",
|
|
99
|
+
"explicit native messaging authorization is required",
|
|
100
|
+
"a required native messaging host resource is unavailable",
|
|
101
|
+
"the selected messaging provider is unavailable",
|
|
102
|
+
"the requested messaging configuration is not qualified",
|
|
103
|
+
"messaging response exceeds the supported wire limits",
|
|
104
|
+
"an admitted captured instance intent is required for execution",
|
|
105
|
+
"an explicit private-team binding is required",
|
|
106
|
+
"selected binding and inline captured invocation are required",
|
|
107
|
+
"an explicit captured instance home is required",
|
|
108
|
+
"captured messaging requires explicit delivery: session",
|
|
109
|
+
"selected wider memberships need their explicitly qualified native setup; they were not omitted",
|
|
110
|
+
"caller-owned OATS_CLI_BIN and readable kernel version are required",
|
|
111
|
+
"oats >=0.24.2 is required for captured HOME custody and retained runtime inspection",
|
|
112
|
+
"the exact retained runtime profile must be readable",
|
|
113
|
+
"the kernel must report the exact retained resolution",
|
|
114
|
+
"a retained launchSelection runtime/model observation is required",
|
|
115
|
+
"Pi strict print does not support session input; retain messaging and configure an input-capable profile",
|
|
116
|
+
"a supported input-capable ordinary Claude/Codex profile is required"
|
|
117
|
+
]
|
|
77
118
|
},
|
|
78
119
|
"hooks": {
|
|
79
120
|
"spawn": {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
KNOWLEDGE_CONTRACT,
|
|
8
8
|
KNOWLEDGE_CONTRACT_VERSION,
|
|
9
9
|
bindKnowledgeDomain,
|
|
10
|
+
bindingChoiceKey,
|
|
10
11
|
checkKnowledgeRuntime,
|
|
11
12
|
normalizeKnowledgeBindingCandidates,
|
|
12
13
|
normalizeKnowledgeDeclaration,
|
|
@@ -24,6 +25,22 @@ const declarationKinds=new Set(['soul','workspace','adoption','operator']);
|
|
|
24
25
|
const errorCodes=new Set(['needs-configuration','requirement-conflict','invalid-binding','authorization-required','host-requirement-missing','provider-unavailable','provider-not-qualified']);
|
|
25
26
|
const obj=value=>value!==null && typeof value==='object' && !Array.isArray(value);
|
|
26
27
|
const wireError=code=>{throw Object.assign(new Error(code),{wireCode:code});};
|
|
28
|
+
// Private diagnostic marker and closed literal vocabulary. Never serialize a
|
|
29
|
+
// caught exception's message, supplied value, path, alias or unknown key.
|
|
30
|
+
const settingDiagnostic=Symbol('runtime-setting-diagnostic');
|
|
31
|
+
const settingMessages=Object.freeze({
|
|
32
|
+
'bindings-file:missing':'setting bindings-file is required (absolute host path)',
|
|
33
|
+
'bindings-file:invalid':'setting bindings-file must be a normalized absolute host path',
|
|
34
|
+
'state-dir:missing':'setting state-dir is required (absolute host path)',
|
|
35
|
+
'state-dir:invalid':'setting state-dir must be a normalized absolute host path',
|
|
36
|
+
'harvest-runtime:missing':'setting harvest-runtime is required (pi, claude or codex)',
|
|
37
|
+
'harvest-runtime:invalid':'setting harvest-runtime must be pi, claude or codex',
|
|
38
|
+
'harvest-model:invalid':'setting harvest-model must be null or a non-empty string',
|
|
39
|
+
});
|
|
40
|
+
function settingError(reason) {
|
|
41
|
+
if(!Object.hasOwn(settingMessages,reason)) wireError('invalid-binding');
|
|
42
|
+
throw Object.assign(new Error('needs-configuration'),{wireCode:'needs-configuration',[settingDiagnostic]:settingMessages[reason]});
|
|
43
|
+
}
|
|
27
44
|
function keys(value,allowed,required,label) {
|
|
28
45
|
if(!obj(value)) wireError('invalid-binding');
|
|
29
46
|
for(const key of Object.keys(value)) if(!allowed.includes(key)) wireError('invalid-binding');
|
|
@@ -100,13 +117,29 @@ function contract(value,{required=false}={}) {
|
|
|
100
117
|
function runtimeSettings(settings) {
|
|
101
118
|
keys(settings,['bindings-file','state-dir','harvest-runtime','harvest-model'],[], 'OKF settings');
|
|
102
119
|
const descriptorFile=settings['bindings-file'],stateDir=settings['state-dir'],runtime=settings['harvest-runtime'],model=settings['harvest-model'] ?? null;
|
|
103
|
-
|
|
120
|
+
for(const name of ['bindings-file','state-dir']) {
|
|
121
|
+
if(settings[name]===undefined) settingError(`${name}:missing`);
|
|
122
|
+
if(!absolute(settings[name])) settingError(`${name}:invalid`);
|
|
123
|
+
}
|
|
124
|
+
if(runtime===undefined) settingError('harvest-runtime:missing');
|
|
125
|
+
if(!['pi','claude','codex'].includes(runtime)) settingError('harvest-runtime:invalid');
|
|
126
|
+
if(model!==null && (typeof model!=='string' || !model.trim())) settingError('harvest-model:invalid');
|
|
104
127
|
return {descriptorFile,stateDir,execution:{runtime,model}};
|
|
105
128
|
}
|
|
129
|
+
// Shared maps contain other providers' opaque values. Ownership comes from
|
|
130
|
+
// this source's declared/default/required knowledge addresses, not from a value
|
|
131
|
+
// looking like a locator or a blanket stores.* prefix. Validate OWN values later.
|
|
132
|
+
function ownedBindings(bindings,ownedKeys) {
|
|
133
|
+
return Object.fromEntries(Object.entries(bindings).filter(([name])=>{
|
|
134
|
+
let key;try{key=bindingChoiceKey(name);}catch{return false;}
|
|
135
|
+
return ownedKeys.has(key);
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
106
138
|
function normalizePhase(req) {
|
|
107
139
|
keys(req.input,['declarations','context'],['declarations','context'],'normalize input');
|
|
108
140
|
if(!Array.isArray(req.input.declarations) || !obj(req.input.context)) wireError('invalid-binding');
|
|
109
|
-
const requirements=[],candidates=[];let domain=null;
|
|
141
|
+
const requirements=[],candidates=[],bindingInputs=[];let domain=null;
|
|
142
|
+
const collect=input=>{if(!obj(input.bindings)) wireError('invalid-binding');bindingInputs.push(input);};
|
|
110
143
|
for(const raw of req.input.declarations) {
|
|
111
144
|
const item=declaration(raw);
|
|
112
145
|
if(item.kind==='soul') {
|
|
@@ -124,14 +157,19 @@ function normalizePhase(req) {
|
|
|
124
157
|
if(rawStore.contract!==KNOWLEDGE_CONTRACT) return;
|
|
125
158
|
const store=contract(rawStore);
|
|
126
159
|
keys(store.payload,['bindings'],['bindings'],'workspace OKF payload');
|
|
127
|
-
|
|
160
|
+
collect({bindings:store.payload.bindings,kind:'workspace-default',origin:item.origin,origins:item.origins,pointer:`/knowledge/stores/${index}/payload/bindings`});
|
|
128
161
|
});
|
|
129
162
|
continue;
|
|
130
163
|
}
|
|
131
164
|
if(item.value.bindings===undefined) continue;
|
|
132
|
-
|
|
165
|
+
collect({bindings:item.value.bindings,kind:item.kind==='adoption'?'import-adoption':'operator',origin:item.origin,origins:item.origins,pointer:'/bindings'});
|
|
133
166
|
}
|
|
134
167
|
if(!domain) wireError('needs-configuration');
|
|
168
|
+
// Collect first so declaration order cannot change who owns a binding. Keep
|
|
169
|
+
// all declared addresses, including dotted aliases and explicit/implicit
|
|
170
|
+
// write.default or custom inheritance; the kernel still resolves precedence.
|
|
171
|
+
const ownedKeys=new Set([...domain.requirements,...domain.candidates].map(item=>item.key));
|
|
172
|
+
for(const input of bindingInputs) candidates.push(...normalizeKnowledgeBindingCandidates({...input,bindings:ownedBindings(input.bindings,ownedKeys)}));
|
|
135
173
|
requirements.push(...domain.requirements);candidates.unshift(...domain.candidates);
|
|
136
174
|
return {requirements,candidates,model:{domain,runtime:runtimeSettings(req.settings)}};
|
|
137
175
|
}
|
|
@@ -152,12 +190,20 @@ function bindPhase(req) {
|
|
|
152
190
|
const runtime=renderKnowledgeRuntime({domain:bound.payload,...req.input.model.runtime});
|
|
153
191
|
return {payloadContract:bound.contract,payloadVersion:bound.version,payload:{...bound.payload,runtime,execution:req.input.model.runtime.execution},credentialRefs:bound.credentialRefs,provenance:bound.provenance};
|
|
154
192
|
}
|
|
155
|
-
function bindingPayload(binding) {
|
|
193
|
+
function bindingPayload(binding,{diagnoseSettings=false}={}) {
|
|
156
194
|
keys(binding,['schemaVersion','capability','payloadContract','payloadVersion','payload','credentialRefs','provenance'],['schemaVersion','capability','payloadContract','payloadVersion','payload','credentialRefs','provenance'],'provider binding');
|
|
157
195
|
if(binding.schemaVersion!==1 || binding.capability!==CAPABILITY || binding.payloadContract!==KNOWLEDGE_CONTRACT || binding.payloadVersion!==KNOWLEDGE_CONTRACT_VERSION || !Array.isArray(binding.provenance)) wireError('invalid-binding');
|
|
158
196
|
if(!obj(binding.credentialRefs) || Object.keys(binding.credentialRefs).length) wireError('invalid-binding');
|
|
159
197
|
keys(binding.payload,['owner','stores','reads','owns','runtime','execution'],['owner','stores','reads','owns','runtime','execution'],'OKF binding payload');
|
|
160
198
|
const domain={owner:binding.payload.owner,stores:binding.payload.stores,reads:binding.payload.reads,owns:binding.payload.owns};
|
|
199
|
+
if(diagnoseSettings) {
|
|
200
|
+
const bound=binding.payload;
|
|
201
|
+
if(!obj(bound.runtime) || !obj(bound.runtime.bindings) || !obj(bound.execution)) wireError('invalid-binding');
|
|
202
|
+
// Check the retained values, not mutable request settings. Existing full
|
|
203
|
+
// envelope/canonical-runtime/execution guards still run below on success.
|
|
204
|
+
runtimeSettings({'bindings-file':bound.runtime.descriptorFile,'state-dir':bound.runtime.bindings.stateDir,
|
|
205
|
+
'harvest-runtime':bound.execution.runtime,'harvest-model':bound.execution.model});
|
|
206
|
+
}
|
|
161
207
|
const runtime=renderKnowledgeRuntime({domain,stateDir:binding.payload.runtime?.bindings?.stateDir,descriptorFile:binding.payload.runtime?.descriptorFile});
|
|
162
208
|
if(!sameJson(runtime,binding.payload.runtime)) wireError('invalid-binding');
|
|
163
209
|
keys(binding.payload.execution,['runtime','model'],['runtime','model'],'OKF worker execution');
|
|
@@ -211,7 +257,7 @@ function checkPhase(req) {
|
|
|
211
257
|
keys(req.input,['binding','context','action','invocation'],['binding','context','action'],'check input');
|
|
212
258
|
if(!obj(req.input.context) || !obj(req.input.action)) wireError('invalid-binding');
|
|
213
259
|
if(Object.hasOwn(req.input,'invocation')) validateInvocationShape(req.input.invocation,{capability:CAPABILITY,context:req.input.context,action:req.input.action});
|
|
214
|
-
const {runtime}=bindingPayload(req.input.binding),action=req.input.action,name=providerActionName(action);
|
|
260
|
+
const {runtime}=bindingPayload(req.input.binding,{diagnoseSettings:true}),action=req.input.action,name=providerActionName(action);
|
|
215
261
|
const harvestInvocation=req.input.invocation;
|
|
216
262
|
const admittedHarvest=name==='harvest' && action.kind==='operation' && action.slot==='knowledge' && action.name==='harvest'
|
|
217
263
|
&& harvestInvocation?.subject.kind==='persistent' && harvestInvocation.instance!==null && !!harvestInvocation.intent;
|
|
@@ -244,6 +290,10 @@ export function handleBindingRequest(phase,value) {
|
|
|
244
290
|
}
|
|
245
291
|
function response(phase,body) {return {schemaVersion:1,phase,slot:SLOT,capability:CAPABILITY,...body};}
|
|
246
292
|
function errorCode(error) {if(errorCodes.has(error?.wireCode)) return error.wireCode;if(error?.code==='E_OWNER') return 'requirement-conflict';return 'invalid-binding';}
|
|
293
|
+
function errorProblem(error) {
|
|
294
|
+
const code=errorCode(error),message=error?.[settingDiagnostic];
|
|
295
|
+
return {code,...(code==='needs-configuration' && Object.values(settingMessages).includes(message)?{message}:{})};
|
|
296
|
+
}
|
|
247
297
|
function enforceOutputLimits(value,depth=1,state={entries:0}) {
|
|
248
298
|
if(depth>BINDING_WIRE_LIMITS.depth) wireError('provider-not-qualified');
|
|
249
299
|
if(value===null || typeof value!=='object') return;
|
|
@@ -259,7 +309,7 @@ export async function runBindingWire(phase,input=process.stdin,output=process.st
|
|
|
259
309
|
for await(const chunk of input) {const bytes=Buffer.from(chunk);length+=bytes.length;if(length>BINDING_WIRE_LIMITS.bytes) wireError('invalid-binding');chunks.push(bytes);}
|
|
260
310
|
const result=handleBindingRequest(phase,parseBindingJson(Buffer.concat(chunks,length)));
|
|
261
311
|
answer=response(phase,{ok:true,result});
|
|
262
|
-
} catch(error) {answer=response(phases.has(phase)?phase:'check',{ok:false,error:
|
|
312
|
+
} catch(error) {answer=response(phases.has(phase)?phase:'check',{ok:false,error:errorProblem(error)});}
|
|
263
313
|
let bytes;
|
|
264
314
|
try {enforceOutputLimits(answer);bytes=Buffer.from(JSON.stringify(answer)+'\n');if(bytes.length>BINDING_WIRE_LIMITS.bytes) wireError('provider-not-qualified');}
|
|
265
315
|
catch {bytes=Buffer.from(JSON.stringify(response(phases.has(phase)?phase:'check',{ok:false,error:{code:'provider-not-qualified'}}))+'\n');}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"capability": "oats.okf",
|
|
3
3
|
"command": "okf",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.2",
|
|
5
5
|
"compatibility": {
|
|
6
|
-
"oats": ">=0.24.
|
|
6
|
+
"oats": ">=0.24.4"
|
|
7
7
|
},
|
|
8
8
|
"layer": "knowledge",
|
|
9
9
|
"description": "External OKF bases with owned nodes, immutable reader views, durable per-source evidence, independent workers, verified Git PR and recoverable non-Git directory delivery.",
|
|
@@ -54,7 +54,16 @@
|
|
|
54
54
|
"version": 1,
|
|
55
55
|
"normalize": "binding-normalize",
|
|
56
56
|
"bind": "binding-bind",
|
|
57
|
-
"check": "binding-check"
|
|
57
|
+
"check": "binding-check",
|
|
58
|
+
"reasons": [
|
|
59
|
+
"setting bindings-file is required (absolute host path)",
|
|
60
|
+
"setting bindings-file must be a normalized absolute host path",
|
|
61
|
+
"setting state-dir is required (absolute host path)",
|
|
62
|
+
"setting state-dir must be a normalized absolute host path",
|
|
63
|
+
"setting harvest-runtime is required (pi, claude or codex)",
|
|
64
|
+
"setting harvest-runtime must be pi, claude or codex",
|
|
65
|
+
"setting harvest-model must be null or a non-empty string"
|
|
66
|
+
]
|
|
58
67
|
},
|
|
59
68
|
"inject": "injects/okf.md",
|
|
60
69
|
"helperInjection": {
|
|
@@ -178,7 +178,21 @@
|
|
|
178
178
|
"version": { "const": 1 },
|
|
179
179
|
"normalize": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
180
180
|
"bind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
181
|
-
"check": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" }
|
|
181
|
+
"check": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
182
|
+
"reasons": {
|
|
183
|
+
"description": "Fixed nonsecret diagnostic strings allowed across the binding wire by exact match only. 1-64 unique printable ASCII strings of 1-200 characters, no braces/interpolation, operator values or paths. Omission uses the kernel's per-capability compatibility list when available; an invalid declaration never falls back.",
|
|
184
|
+
"type": "array",
|
|
185
|
+
"minItems": 1,
|
|
186
|
+
"maxItems": 64,
|
|
187
|
+
"uniqueItems": true,
|
|
188
|
+
"items": { "type": "string", "minLength": 1, "maxLength": 200, "not": { "pattern": "[{}]|[^\\x20-\\x7e]" } }
|
|
189
|
+
},
|
|
190
|
+
"keys": {
|
|
191
|
+
"description": "Owned operator-key declarations: exact names or trailing-dot namespaces, no other pattern syntax. Kernel 0.24.4 validates shape only; filtering, overlap checks and ownership attribution are deferred to 0.25. Providers must ignore foreign keys themselves.",
|
|
192
|
+
"type": "array",
|
|
193
|
+
"uniqueItems": true,
|
|
194
|
+
"items": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9_-]*\\.?$(?![\\s\\S])" }
|
|
195
|
+
}
|
|
182
196
|
}
|
|
183
197
|
},
|
|
184
198
|
"operations": {
|
|
@@ -36,7 +36,34 @@ result. All structured responses exit 0: transport completed, while `ok` is the
|
|
|
36
36
|
semantic outcome. Nonzero exit/signal/timeout is transport failure. Allowed error/problem codes are needs-configuration, requirement-conflict,
|
|
37
37
|
invalid-binding, authorization-required, host-requirement-missing,
|
|
38
38
|
provider-unavailable and provider-not-qualified. Optional provider error/problem
|
|
39
|
-
`message` is permitted
|
|
39
|
+
`message` is permitted. The 0.24.4 follow-up retains it ONLY when it exactly
|
|
40
|
+
matches a fixed nonsecret reason in the VERIFIED selected capability manifest's
|
|
41
|
+
optional `binding.reasons` array: **1–64 unique strings**, each **1–200 printable
|
|
42
|
+
ASCII characters**, with no braces/interpolation markers. If the field is absent,
|
|
43
|
+
the kernel's reviewed per-capability compatibility list applies; a present invalid
|
|
44
|
+
or empty declaration refuses, never falls back. No trimming, Unicode normalization,
|
|
45
|
+
interpolation, prefix matching, operator values, paths, or unlisted provider output
|
|
46
|
+
cross this boundary. Code-only replies
|
|
47
|
+
and unknown/unlisted messages keep the existing kernel template fallback.
|
|
48
|
+
|
|
49
|
+
The allowlist is out-of-band kernel input, never declared by a provider response.
|
|
50
|
+
Exact artifact approval is still required BEFORE invoking the codec. The broker
|
|
51
|
+
preserves the vetted message and preparation rechecks it against the same selected
|
|
52
|
+
manifest, retaining slot/capability/origins. JSON and human CLI diagnostics surface
|
|
53
|
+
the reason; human output also shows an existing choice `key` when provided. This
|
|
54
|
+
changes no readiness status, launch authority, credential contract or wire version.
|
|
55
|
+
Older kernels reject the new optional manifest fields; providers declaring them
|
|
56
|
+
must floor on the reasons-capable **0.24.4** kernel. The compatibility list serves
|
|
57
|
+
older manifests, not a way around declaration validation. It includes the complete
|
|
58
|
+
30-message aweb1.11.0 codec vocabulary (including non-ready check reasons) and the
|
|
59
|
+
seven OKF2.1.2 setting messages; it invents none for code-only OKF2.1.1.
|
|
60
|
+
|
|
61
|
+
`binding.keys` is also accepted with **shape validation only** in0.24.4: a unique
|
|
62
|
+
array of exact names or trailing-dot namespaces (`wider`, `stores.`), each matching
|
|
63
|
+
`^[A-Za-z][A-Za-z0-9_-]*\\.?$` over the WHOLE string (no trailing newline). There
|
|
64
|
+
is no filtering, overlapping-ownership check or owned/unowned-key attribution yet;
|
|
65
|
+
those are deferred to0.25. Providers still receive the complete map and MUST ignore
|
|
66
|
+
foreign keys themselves. Declaring keys does not authorize diagnostic text.
|
|
40
67
|
|
|
41
68
|
Knowledge providers and harvesters follow the same separation: the
|
|
42
69
|
[knowledge capability boundary](2026-09-16-knowledge-capability-contract.md) keeps
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Purpose:** the one accurate view of every work stream in the redesign, what is on main, what is in flight, who owns it, and what blocks it. Lead: `oats-expert` (redesign lead). Updated whenever anything merges, is returned, or reality changes. Older per-lane boards are superseded by this file.
|
|
4
4
|
|
|
5
|
-
**Last update:** 2026-09-21
|
|
5
|
+
**Last update:** 2026-09-21 23:40Z · main `cd3d4913`+ · **oats-knowledge main `7148a36` = centralised base (58 concepts)** · OATS v0.24.4 · OKF v2.1.2 · aweb v1.11.2 · framework v1.1.3 · imports @ `7416d84e`
|
|
6
6
|
|
|
7
7
|
Legend: ✅ on main/published · 🔄 in flight (PR/branch) · 🟡 preserved, not adopted · ⬜ not started · ⛔ blocked
|
|
8
8
|
|
|
@@ -11,12 +11,12 @@ Legend: ✅ on main/published · 🔄 in flight (PR/branch) · 🟡 preserved, n
|
|
|
11
11
|
| # | Stream | State | Owner | Next action |
|
|
12
12
|
|---|---|---|---|---|
|
|
13
13
|
| S1 | Knowledge capability contract rework (kernel↔provider boundary, OKF 2.x) | ✅ OATS 0.24.1 / OKF 2.1.1 published | P | done for this phase |
|
|
14
|
-
| S2 | Workspace/Portable Souls adoption of the OATS repos | ✅
|
|
14
|
+
| S2 | Workspace/Portable Souls adoption of the OATS repos | ✅ **FIRST SECOND-OPERATOR PUBLICATION (735296c5, 2026-09-21)**: fresh machine, published definition alone, no `launch` → `status: prepared`, resolution `sha256-f11c433c…`, zero problems. ❌ then OKF `check` → `needs-configuration / provider-not-qualified`, **no message/key/origins**, identical for `harvest-runtime` pi and claude — OKF's check phase is code-only and folds ≥4 causes into one code (P, OKF 2.1.3). ⏳ Pi `launch` blocked by kernel ifInstalled defect (L, 0.24.5). Earlier: ✅ knowledge: bound to public `oats-knowledge` via OKF 2.1.2 from a fresh machine, four kernel versions · ✅ messaging normalizes; `wider` deadlock gone; named reasons in one run · ❌ **operator's amended verdict (9816b8ec): no resolution can publish — behind `responsibleHuman`, helper composition refuses because `oats.core` and `oats.aweb` ship `inject` without `helperInjection`** (OKF adopted the contract; its siblings did not). Both remaining blockers are packaging, not operator input. Fix: [decision](https://github.com/awebai/oats/blob/main/agents/oats-expert/soul/knowledge/decisions/helper-injection-policy-on-every-injecting-capability.md) — core `inherit`, aweb `omit`, framework release check, attributed early refusal · then unchanged re-run | lead, Antares | oats.framework 1.1.3 + aweb 1.11.2 + pins; re-run |
|
|
15
15
|
| S3 | Messaging capability readiness on the new infrastructure (aweb) | ✅ aweb 1.11.0 released · ✅ **catalog + six editions pin v1.11.0 (0.24.2)** · ⬜ second-operator re-run | P, lead | Antares re-run |
|
|
16
16
|
| S4 | Official capabilities `oats.core` / `oats.setup` + explicit default + onboarding `oats-setup-expert` | ✅ D1, D2, **D3 merged (PR35)**: `oats onboard` verified live (acquire 1.1.1 → setup expert with both caps → scaffold composes the five capability skills, no legacy) · `oats.framework` 1.1.1 tagged | P, L | done; Desktop surfaces → S8 |
|
|
17
17
|
| S5 | Official marketplace = reviewed list in oats repo | ✅ D4 merged · ✅ `oats.framework` 1.1.1 listed (`oats.core`, `oats.setup`, `oats.knowledge-theory` aliases) | M | Desktop view → S8 |
|
|
18
18
|
| S6 | Five expert souls created in the oats repo (`souls/<name>/`) | ✅ five + `oats-setup-expert` on main, all declaring `oats.core`, exported + imported | M, L, lead | legacy `agents/` cutover after S7 proof |
|
|
19
|
-
| S7 | Centralised per-soul knowledge in `oats-knowledge` (migration + PR-only learning) | ✅ **
|
|
19
|
+
| S7 | Centralised per-soul knowledge in `oats-knowledge` (migration + PR-only learning) | ✅ **MIGRATED — PR #2 merged → main `7148a36`, 58 concepts / 35k words from 399 legacy** (kernel 26, expert 18, desktop 13, assistant 1, market-research chartered empty); option A roster; theory + no-code-teaching rule; two workflow passes (24 agents) + lead review; validator 58/0/0; ownership 14/14 · ⏸ harvest OFF until new souls run from the base (human) | lead | next: legacy `agents/` decommission with the `~/OATS` cutover |
|
|
20
20
|
| S8 | Desktop parity (marketplace view, soul creation with `oats.core`, onboarding flow) | ⬜ after D3 · **host offered: Juan's machine (has `claude`)** — accepted | fresh Desktop engineer on Juan's host | brief + spawn params after D3 |
|
|
21
21
|
|
|
22
22
|
## S1 — Knowledge capability contract rework
|
|
@@ -33,12 +33,67 @@ Legend: ✅ on main/published · 🔄 in flight (PR/branch) · 🟡 preserved, n
|
|
|
33
33
|
- ⬜ Fresh local deployment from the shared definition (P1.5) — the real acceptance gate.
|
|
34
34
|
|
|
35
35
|
## S2 — second-operator gate report (Antares, Juan's machine, 2026-09-20)
|
|
36
|
-
Fresh dir, local `@awebai/oats@0.24.1`, no prior state. `inspect --request` → ready-for-preparation, membership eligible, source `souls/oats-kernel-expert@caa341f3`. `prepare` resolved and materialized `oats-package@caa341f3`, `oats.okf@v2.1.1`, `oats.aweb@v1.10.3`, `oats.core`, `oats.setup`, `oats.knowledge-theory` + soul; artifact-set approvals worked. Terminal: `needs-configuration` + `provider-not-qualified` (aweb 1.10.3 has no binding interface) — expected. Seams
|
|
36
|
+
Fresh dir, local `@awebai/oats@0.24.1`, no prior state. `inspect --request` → ready-for-preparation, membership eligible, source `souls/oats-kernel-expert@caa341f3`. `prepare` resolved and materialized `oats-package@caa341f3`, `oats.okf@v2.1.1`, `oats.aweb@v1.10.3`, `oats.core`, `oats.setup`, `oats.knowledge-theory` + soul; artifact-set approvals worked. Terminal: `needs-configuration` + `provider-not-qualified` (aweb 1.10.3 has no binding interface) — expected. Seams — **all fixed in PR36 (913c4f9e), shipped in 0.24.3**:
|
|
37
37
|
1. `inspect --request` requires `workTarget`; `prepare --request` refuses it (`buildFreshPreparationRequest` exists but the CLI never uses it).
|
|
38
38
|
2. `prepare` on the absent deployment inspect blessed → raw `ENOENT` + host path through the JSON envelope.
|
|
39
39
|
3. `prepare` writes lock v3; `oats trust <cap> --dir` rejects it (`unsupported lockfileVersion 3`) → dead end from `--help`.
|
|
40
40
|
4. The working `trust --deployment --artifact-set <sha256>` route is absent from `--help`.
|
|
41
|
-
5. Problems carry `origins: []` and no slot/capability, so
|
|
41
|
+
5. Problems carry `origins: []` and no slot/capability, so with more than one unresolved slot the operator cannot tell which slot a `needs-configuration` refers to (Antares needed three runs and an ablation table). **Root cause of the original identical pair (L's trace, confirmed by P and by Antares' settings-aware run):** both OKF `normalize` calls refused because the request had no OKF runtime settings (`bindings-file`, `state-dir`); the identical pair is correct output for that input. With settings, OKF normalize diagnostics DO reach the operator (a malformed `acceptedBranch` yields a distinct `invalid-binding`); a structurally valid locator to a nonexistent repo is correctly not distinguished until OKF `check`. Fix (0.24.3, PR36) = attribution by slot/capability/phase/origins + kernel-fixed messages. Precision: the kernel names the missing item only where it knows it (missing binding interface: id/version/slot); for a provider `needs-configuration` it cannot name `bindings-file`/`state-dir` because OKF 2.1.1's wire is code-only — OKF 2.1.2 (assigned to P) adds the fixed safe message naming the setting. Antares' earlier "no signal at all" framing is withdrawn by its author.
|
|
42
|
+
|
|
43
|
+
## S2 — second-operator re-run on 0.24.2 (Antares, Juan's machine, 2026-09-21)
|
|
44
|
+
- Found the five expert imports still pinned at `caa341f3` (aweb 1.10.3) while the setup expert was at `0aad753c` — the one repinned soul was the one that never exercises aweb. Fixed: all six imports at v0.24.3 `3156e4de` (638206b9); new layout guard fails when a pin's provider requirements lag the current edition (3ce40aaa, verified to catch the miss).
|
|
45
|
+
- **aweb 1.11.0 qualifies**: via the direct source route on current main, `provider-not-qualified` disappeared → `approval-required` → after approval both slots on the settings hold. Confirms L's root cause.
|
|
46
|
+
- `oats onboard --workspace git:github.com/awebai/oats` from a fresh dir: clean (acquired 1.1.1, both caps, spawn printed not run); scaffold composed exactly the five capability skills, both trusted, no hooks.
|
|
47
|
+
- Seams 1–5 reproduced identically on 0.24.2 (baseline); fixed in 0.24.3. Gate decisions given: `harvest-model` arbitrary for the gate; aweb slot `delivery: session` without a private-team binding → expected typed `needs-configuration` naming the binding (Juan's team identity is never guessed).
|
|
48
|
+
|
|
49
|
+
## S2 — settings-aware run (Antares, direct source route on main, aweb 1.11.0, 2026-09-21)
|
|
50
|
+
- With `bindings-file`/`state-dir`/`harvest-runtime: pi` and the public `oats-knowledge` bound as base `oats`: **the knowledge slot binds** (OKF problem gone). `harvest-model` is optional per manifest; aweb 1.11.0 declares exactly one setting (`delivery`).
|
|
51
|
+
- **aweb 1.11.0 is the only remaining hold** (`needs-configuration`, identical for `delivery: session` and `channel` on 0.24.2's unattributed output). No resolution publishes → OKF `check` probe not reachable yet. 0.24.3 run will show the attributed aweb message (expected: private-team binding / Pi session-input).
|
|
52
|
+
|
|
53
|
+
## Cutover started: `~/OATS` onboarded from the public workspace (lead, 2026-09-21 23:55Z)
|
|
54
|
+
|
|
55
|
+
Kernel 0.24.4 installed globally; `oats onboard --dir ~/OATS --workspace git:github.com/awebai/oats` → `oats.framework` 1.1.3 acquired and exact-locked (lock v2), `oats.core` + `oats.setup` selected, `oats-setup-expert` copied from the workspace's pinned edition (`7416d84e`, workspace revision `c82a96af`), layers `none`. First attempt refused `integrity-drift` because the kernel's **bundled catalog snapshot** still listed framework 1.1.1 — correct refusal, misleading read; remedy `OATS_PACKAGE_CATALOG=<main's list>`; lesson `lessons/bundled-catalog-lags-the-reviewed-list.md` (proposes onboarding read the catalog from the workspace revision). Next: spawn the setup expert to adopt the workspace (knowledge → public `oats-knowledge` via OKF 2.1.2; messaging → aweb 1.11.2 or none) with explicit approvals; then the five expert souls run from the central base and this checkout becomes secondary. Harvest stays off until then.
|
|
56
|
+
|
|
57
|
+
## S2 — FIRST SECOND-OPERATOR PUBLICATION + `check` finding (Antares, 735296c5, 2026-09-21)
|
|
58
|
+
|
|
59
|
+
Verified wave (aweb v1.11.2, framework v1.1.3, main cd3d4913, imports 7416d84e). Fresh deployment, unchanged request minus `launch`, synthetic `responsibleHuman`, `wider: []` → `{"status":"prepared","resolution":{"id":"sha256-f11c433c…"},"executionBinding":{…},"problems":[]}`, exit 0, empty stderr. **An operator holding none of the authoring state produced an executable resolution from the published workspace definition — the first.** Helper-injection fix confirmed from outside; `ignored` shrank to `["operator"]` correctly.
|
|
60
|
+
|
|
61
|
+
Then P's OKF `check` probe against the published resolution (contacted the public repository): `{"status":"needs-configuration","problems":[{"code":"provider-not-qualified"}]}` — bare; the operator dumped the shape to prove nothing was elided. Second resolution with `harvest-runtime: claude`, no model (`sha256-1aa5ab9a…`, prepared) → identical. Traced by lead: OKF 2.1.2 `checkPhase` (`binding-wire.mjs` L256–282) returns `problem(code)` only and maps unadmitted action / >64 bases / `E_OWNER|E_BASE|E_VALIDATION|E_DIRECTORY_GIT|E_CONFIRM` all to `provider-not-qualified`; the kernel's check path already passes declared reasons. Fifth of the family. Operator's verdict adopted: *"A second operator reaches, resolves, approves and publishes an executable resolution from the public definition alone … The OKF check readiness probe then refuses as provider-not-qualified with no reason, no key and no origins, and the refusal is independent of the harvest runtime."* Decision amended: every check problem carries a fixed reason distinct per cause. **Assigned P: OKF 2.1.3** — ahead of 0.24.5 by the operator's priority, which the lead accepts (publication works; check is the next thing every operator hits). **Operator narrowed the cause from outside (9c9dc404), each elimination tested:** not action kind, not base count, not `validateBase` (validator from the 2.1.2 artifact against the public `knowledge/`: 25 concepts, 0/0), not directory-git, not runtime; staging IS reached (nonexistent repo → `unavailable/provider-unavailable` in 0.65s; real → `needs-configuration/provider-not-qualified` in 1.66s). Left: `verifyPublicationTree` (E_CONFIRM/E_BASE) or `checkKnowledgeRuntime` (E_OWNER; not a naive owner mismatch — public base owner = edition owner). Forwarded to P so 2.1.3 reasons split exactly these. Sharp edge noted: the validator at the repository root fails on bundle-root-absolute links — a wrong root will look like an invalid base. **Acceptance plan corrected by the operator (bbbb6bdd):** the two retained resolutions captured and approved the exact 2.1.2 artifact and `runCapturedProviderBinding` executes exactly that, so re-probing them must still return code-only — that is immutability working. After 2.1.3 is pinned: keep the old resolutions as before-evidence, prepare NEW resolutions from the same two request shapes with only the OKF source moved to v2.1.3, approve their exact artifact sets, run the same probe → valid before/after.
|
|
62
|
+
|
|
63
|
+
## Helper-injection gap FIXED + next blocker found (lead, 2026-09-21 20:30Z)
|
|
64
|
+
|
|
65
|
+
- ✅ **oats.framework 1.1.3** (`oats-framework/v1.1.3` @ `6108bfbb`): `oats.core` 1.0.1 declares `helperInjection: inherit`. ✅ **aweb 1.11.2** (`v1.11.2` @ `a671884`): `helperInjection: omit`, manifest-only. Bundled copy byte-synced; catalog `oats.aweb` → v1.11.2, `oats.framework` → v1.1.3; six editions → aweb v1.11.2; six imports → `7416d84e`. Release check added: every injecting framework capability declares its helper policy (jira/linear/review pending upstream, not required by any edition). Notes: `docs/release-notes/oats-framework-v1.1.3.md`.
|
|
66
|
+
- **Lead probe from the published 0.24.4 tarball, operator's request shape + synthetic `responsibleHuman` + `wider: []`:** helper composition now passes. **Without a `launch` block the resolution PUBLISHES** (`status: prepared`, resolution + execution binding) — first end-to-end publication from public definitions. **With the Pi `launch` block it refuses** on a bare `needs-configuration: runtime package requirements need retained runtime roots and a qualified loader`: kernel defect — the captured path treats aweb's `ifInstalled: true` Pi floor (`npm:@awebai/pi ≥0.3.10`, only if present) as a hard block, and refuses with no `details`. `delivery: channel` gives the byte-identical message. Lesson: `lessons/captured-launch-ignores-ifinstalled-and-refuses-bare.md`. **Assigned to L with the retirement defect → 0.24.5 (kernel), plus attribution for both bare refusals.** Operator's re-run: after 0.24.5, or now without `launch` to witness publication.
|
|
67
|
+
|
|
68
|
+
## S2 — operator's self-correction (9816b8ec, 2026-09-21): packaging gap behind `responsibleHuman`
|
|
69
|
+
|
|
70
|
+
The operator supplied a deliberately synthetic `responsibleHuman` (`gate-probe-not-a-real-human`; accepted — `humanRef` checks only provider + non-empty id, i.e. an accountability claim the code records but does not verify) and preparation refused: `needs-configuration: new helper injection requires an explicit capability policy` (`lib/helper-injection-policy.mjs:38`), a bare top-level error with no `details`/attribution. Cause verified from the published manifests: `oats.okf` declares `helperInjection {omit}`; **`oats.core` (`injects/oats.md`) and `oats.aweb` (`injects/aweb.md`) declare none**. Every edition declares `oats.core`, so no edition could publish once its OKF harvest helper composed. Fourth finding of the same family (contract adopted by one provider, not its siblings). Operator's restated verdict adopted verbatim: *"A second operator reaches, resolves and approves the entire graph from the public definition, binds knowledge to the public `oats-knowledge` base, and normalizes messaging. Preparation cannot publish a resolution … Both remaining blockers are packaging, not operator input."* The earlier "EXIT GATE MET" entry below is superseded by this one.
|
|
71
|
+
|
|
72
|
+
## Kernel defect found at peer retirement (2026-09-21) — 0.25 lifecycle item
|
|
73
|
+
|
|
74
|
+
`oats retire oats-expert-scheduler-peer` (self and spawner retry) fails closed: *recovered Git index/status disagreed with the source*. Cause: `preserveRetirementWork` clones the branch recorded in `instance.json` (`feat/portable-scheduler-captures`, tip e0c3232c) while the worktree is checked out on `docs/okf-host-runtime-settings` (5919a547); the status comparison cannot agree. Verified by hand: worktree clean, both branches pushed, every deliverable merged (oats PR39, oats-okf PR5, oats-aweb PR4); the only dirt is two untracked files in a nested scratch clone that are already on aweb main since 1.11.0. **Nothing unpreserved.** Instance left `RETIRING`; no branch/metadata/force surgery. Fix scoped to **0.25 (L)**: derive the recovery branch from the worktree, record drift as a typed observation. Lesson: `lessons/retire-recovery-uses-recorded-branch-not-checked-out-branch.md`.
|
|
75
|
+
|
|
76
|
+
## S2 — acceptance run on the 0.24.4 wave (Antares, Juan's machine, 2026-09-21) — superseded by the self-correction above (knowledge result stands)
|
|
77
|
+
|
|
78
|
+
Fresh directory and deployment, `@awebai/oats@0.24.4`, policy sources v2.1.2 / v1.11.1, `wider: []`, request otherwise unchanged from the 0.24.3 run; wave verified independently first (npm, both provider tags, main `86b5f924`, six imports @ `08c68ece`).
|
|
79
|
+
|
|
80
|
+
- inspect: exit 0, `ignored: ["operator","launch"]`, six imports @ `08c68ece`, `ready-for-preparation` — **seam 1 closed**.
|
|
81
|
+
- prepare → two `approval-required` → approved via the printed `--artifact-set` commands → prepare: **knowledge binds with `wider: []` present (deadlock fixed)**; the single remaining problem is `{code: needs-configuration, slot: messaging, key: /bindings/messaging/responsibleHuman}`.
|
|
82
|
+
- `bindings-file` removed → `setting bindings-file is required (absolute host path)` in one run — the named-reason criterion ("one run instead of six") met. `wider` removed → both messaging keys attributed separately.
|
|
83
|
+
- Operator's observation, adopted: `required value has no concrete binding` is generic text but the `key` carries the address; sufficient — no more spend on the string.
|
|
84
|
+
- **Operator's verdict (verbatim):** *"An outside operator on a fresh machine, holding none of the authoring state, now reaches a single, fully-named, legitimately-operator-owned requirement: `/bindings/messaging/responsibleHuman`. Everything else — source discovery, workspace membership, artifact resolution, exact approval, knowledge binding to the public `oats-knowledge` base through OKF 2.1.2, messaging normalize — resolves from the published definitions alone. That is the S2 exit gate met … not 'it works', but 'every remaining item is named and belongs to the operator'."*
|
|
85
|
+
- Coda: the operator has put `responsibleHuman` to Juan as a real decision; if supplied, R publishes and the OKF `check` probe runs (typed status/problems only). If declined, that is a legitimate end state and is recorded as such.
|
|
86
|
+
- Four acceptance trees retained on the operator's side (0.24.1–0.24.4) — every later check is a repeat, not a rebuild.
|
|
87
|
+
|
|
88
|
+
## S2 — 0.24.3 gate run (Antares, Juan's machine, 2026-09-21) — verdict
|
|
89
|
+
- Seams 1, 3, 4 **fixed**; seam 2 fixed as typed (message does not echo the path — deliberate, consistent with no host diagnostics; expectation corrected); seam 5 **half**: attribution (slot/capability/full origins) fixed; naming the missing item only where the kernel knows it. The specificity half is provider-side: OKF 2.1.2 (assigned) and an aweb follow-up.
|
|
90
|
+
- Six imports resolve at the repinned revision; workspace route selects aweb 1.11.0; **the knowledge slot fully resolves through the workspace route** with `bindings-file`/`state-dir`/`harvest-runtime` (`harvest-model` confirmed optional).
|
|
91
|
+
- **Messaging was a SOURCE defect, not an operator gap**: aweb 1.11.0 normalize requires the workspace to declare `teams: {private: per-human}` and the soul a messaging declaration; neither was published, so the slot was unreachable by any input. Fixed on main **906b1558** (workspace policy + `teams: []` on the five messaging editions; verified against aweb v1.11.0 normalize) and imports repinned **f3ee31e0**. Remaining operator inputs after the fix: `responsibleHuman` (required) and `wider` (required, may be empty) — the private team is a later `check` outcome.
|
|
92
|
+
- **S2 exit-gate verdict (amended per the operator):** knowledge — an outside operator on 0.24.3 discovers, resolves, approves and binds from public sources, remaining item attributed: **PASS**. Messaging — on 0.24.3 as published the operator hit a source defect wearing a configuration error; fixed on main 906b1558 and to be re-proven on the repinned imports before it is written as PASS.
|
|
93
|
+
- **Re-run on 906b1558 (operator, b875669f):** messaging normalize passes; remaining problem is `responsibleHuman`, attributed. But end-to-end is **deadlocked**: `wider: []` (messaging requires) makes OKF 2.1.1 return `invalid-binding` on knowledge (it validates every `operator.bindings` key as a store locator); without it messaging needs `wider`. Operator's wording adopted for S2: *a second operator reaches a published, resolvable configuration boundary for knowledge; messaging is blocked by a kernel-level binding-namespace collision, not by operator input or identity.* Not Juan's to unblock; no identity requested. [Decision](https://github.com/awebai/oats/blob/main/agents/oats-expert/soul/knowledge/decisions/operator-bindings-ownership.md): flat map with declared ownership (`binding.keys`); providers ignore foreign keys (OKF 2.1.2, aweb 1.11.1); kernel attributes/refuses stray keys by name — **deferred to 0.25 by L's scope call (accepted): overlap refusal, filtered forwarding, owned/unowned attribution and the undeclared-provider path need their own regression matrix; the provider ignore rule is the floor and ships in 0.24.4's provider releases.**
|
|
94
|
+
- ✅ **Provider wave published (2026-09-21 17:00Z):** OKF **2.1.2** (oats-okf PR5 → `f02a0d9a`, tag v2.1.2: named-setting reasons, source-owned operator bindings, `binding.reasons`, floor >=0.24.4), aweb **1.11.1** (oats-aweb PR4 → `d404da76`, tag v1.11.1: manifest-only reasons + keys, floor >=0.24.4), **oats.framework 1.1.2** (`oats-framework/v1.1.2` @ `6f98c7c9`), OKF mirror finalized + bundled aweb byte-synced (`08c68ece`), catalog refs, six editions repinned, six imports → `08c68ece`. **Lead's end-to-end probe from the published 0.24.4 tarball via the workspace route, Antares' exact request shape with `wider: []`:** inspect accepts the complete file (`ignored: [operator, launch]`), six imports resolve, both providers approve via the printed `trust` commands, **knowledge binds and messaging normalizes — the deadlock is gone**; the single remaining problem is `key: /bindings/messaging/responsibleHuman` (the operator's own decision). Named reasons cross in one run: `setting bindings-file is required (absolute host path)`; dropping `wider` yields both messaging keys attributed. Awaiting the independent operator's unchanged re-run to write it as PASS. **Seam 1 independently confirmed closed by the operator on 0.24.4** (same file that failed on 0.24.3 → exit 0, `ignored: [operator, launch]`).
|
|
95
|
+
- ✅ **PR41 merged `ef211d3e`** (L): inspect accepts prepare's superset (`ignored`), provider reasons cross the wire by exact match (manifest `binding.reasons` / bundled lists), `binding.keys` shape-only, CLI renders `key`. Full gate 1657/1653/0/4 twice (L + maintainer). → ✅ **v0.24.4 PUBLISHED** (npm both packages, GH release 7 assets, tarball probe: installed validator accepts `binding.reasons`/`keys`, `inspect --request` accepts `operator`/`launch`), kernel-first; OKF 2.1.2 / aweb 1.11.1 floor `>=0.24.4` (closed validator on ≤0.24.3 rejects the fields — P's finding).
|
|
96
|
+
- Two further kernel findings from the repeat (assigned to L, 0.24.4): (a) seam 1 converged one way only — `inspect --request` still refuses prepare's `operator`/`launch`; (b) **the kernel discards the adapter's reason** at the wire (`provider-binding-wire.mjs:23`) and templates it; aweb already sends whitelisted safe reasons. New [decision](https://github.com/awebai/oats/blob/main/agents/oats-expert/soul/knowledge/decisions/provider-problem-reasons-cross-the-wire.md): whitelisted fixed reasons cross the wire (`binding.reasons`), free text still refused.
|
|
42
97
|
|
|
43
98
|
## S3 — Messaging (aweb) on the new infrastructure
|
|
44
99
|
- ✅ **aweb PR3 merged → v1.11.0 (93f8ab96)**: `binding {normalize,bind,check}` on the existing wire; `check` = HOME-route operational custody only (explicit private team, `delivery: session`, kernel ≥0.24.2 via caller-owned `OATS_CLI_BIN`, retained `launchSelection` must be input-capable Claude/Codex; strict-Pi print → `needs-configuration`, never downgraded). Native adapter over existing `aw` commands with physical identity-dir custody and redacted tokens. Standalone 30/0; coupling 14/0 vs kernel b92f0d07. PR33 (launchSelection projection, OATS_CLI_BIN in codec env) merged b92f0d07.
|
|
@@ -13,7 +13,14 @@ oats inspect --request /absolute/inspection.json --json
|
|
|
13
13
|
oats inspect --request /absolute/inspection.json --emit-prepare-request /absolute/preparation.json --json
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
This mode accepts one request file, optional `--emit-prepare-request`, and `--json`.
|
|
16
|
+
This mode accepts one request file, optional `--emit-prepare-request`, and `--json`.
|
|
17
|
+
The 0.24.4 follow-up also accepts the complete preparation request's `operator`,
|
|
18
|
+
`launch`, `helperLaunches`, `mode`, and `allowLocalPaths` fields. Inspection ignores
|
|
19
|
+
their semantics: it does not validate provider payloads, select a runtime/model,
|
|
20
|
+
execute a codec, or authorize local acquisition. `ignored: [...]` lists only the
|
|
21
|
+
present field NAMES in a stable order; values stay out of the metadata view and
|
|
22
|
+
`omitted.*` remains true. Preparation still validates those fields normally.
|
|
23
|
+
Unknown fields remain errors. Explicit captured selectors
|
|
17
24
|
or current-context flags conflict before file reads; inherited captured environment
|
|
18
25
|
is not new-work input. Other existing inspect modes are unchanged. The shared
|
|
19
26
|
bounded strict JSON request reader feeds the existing inspection validator intact:
|
|
@@ -80,6 +87,12 @@ A held inspection cannot emit a fresh preparation request. Core callers can
|
|
|
80
87
|
explicitly request this data via `{includePrepareRequest:true}`; the default
|
|
81
88
|
metadata projection and its omissions are unchanged.
|
|
82
89
|
|
|
90
|
+
The explicit export preserves authored prepare-only fields privately through the
|
|
91
|
+
existing builder, without interpreting them; it must not silently drop operator
|
|
92
|
+
bindings or launch/helper choices. They remain unvalidated until preparation.
|
|
93
|
+
This does not expose their values in normal metadata or turn ignored values into
|
|
94
|
+
inspection authority.
|
|
95
|
+
|
|
83
96
|
The file is reusable new-work input, NOT a stored resolution, approval, admission,
|
|
84
97
|
or serialized ready-inspection permission. Preparation performs fresh validation
|
|
85
98
|
and observations, including re-resolving any mutable source selectors. Provider
|
|
@@ -132,6 +145,10 @@ provenance where available. A no-interface provider is identified with kernel-kn
|
|
|
132
145
|
manifest/version facts. Other supported slots still normalize, resolve through
|
|
133
146
|
the same choice engine, and bind if their own choices are resolved; any required
|
|
134
147
|
slot problem still prevents publication. Provider free text is not passed through.
|
|
148
|
+
The 0.24.4 follow-up preserves only exact fixed reasons declared by the selected
|
|
149
|
+
manifest (or its reviewed kernel compatibility list when absent); see the
|
|
150
|
+
[binding wire](2026-09-16-provider-binding-wire.md). Human CLI output also shows a
|
|
151
|
+
problem's existing choice key, without inventing new key/provider semantics.
|
|
135
152
|
Different opaque inputs need not produce different public errors if both fail the
|
|
136
153
|
same provider prerequisite. In particular, missing OKF host runtime settings can
|
|
137
154
|
hold both syntactically valid Git locators; preparation does not test whether a
|
package/docs/first-team.md
CHANGED
|
@@ -44,14 +44,13 @@ create/spawn/retire. A team roster does not select a work repository for spawn.
|
|
|
44
44
|
|
|
45
45
|
## Onboarding with the setup expert
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
start in an explicit empty deployment:
|
|
47
|
+
On **OATS 0.24.2 or later**, start in an explicit empty deployment:
|
|
49
48
|
|
|
50
49
|
```bash
|
|
51
50
|
oats onboard --dir /absolute/new-deployment --json
|
|
52
51
|
```
|
|
53
52
|
|
|
54
|
-
|
|
53
|
+
`oats onboard` ships from 0.24.2 (earlier kernels refuse it). It is a
|
|
55
54
|
classic local bootstrap, not captured preparation or workspace enrollment. It
|
|
56
55
|
acquires `oats.framework` from the official catalog, exact-locks its artifacts,
|
|
57
56
|
selects only `oats.core` and `oats.setup` for the new local `oats-setup-expert`,
|
|
@@ -80,7 +79,14 @@ the observed revision. Missing or incompatible explicit sources refuse; they do
|
|
|
80
79
|
not fall back to the packaged default. The copied edition's package must match
|
|
81
80
|
the official acquisition; workspace policy, teams and provider adoption values
|
|
82
81
|
are not silently adopted. Without this option, only the packaged definition and
|
|
83
|
-
instruction text are used—no knowledge corpus is bundled.
|
|
82
|
+
instruction text are used—no knowledge corpus is bundled. From 0.24.5, a
|
|
83
|
+
`--workspace` onboarding also reads `package-catalog.json` **from the workspace
|
|
84
|
+
repository at its observed revision** and acquires the `oats.framework` that
|
|
85
|
+
catalog names; the kernel's bundled catalog is only the fallback (it is a
|
|
86
|
+
snapshot at the kernel's own release and lags every framework release cut
|
|
87
|
+
afterwards). `OATS_PACKAGE_CATALOG` still overrides both. The result reports
|
|
88
|
+
`catalog.origin` (`workspace` | `bundled` | `override`), and an integrity
|
|
89
|
+
refusal names the lag when the bundled entry caused it.
|
|
84
90
|
|
|
85
91
|
The manual path below retains its stated older integration/version scope.
|
|
86
92
|
|
package/docs/layers.md
CHANGED
|
@@ -80,7 +80,7 @@ Messaging is conversation, not automatically task state. Accepted knowledge may
|
|
|
80
80
|
|
|
81
81
|
The current slot name is **`messaging`**. The capability owns native identity, addressing, team membership, transport, wake delivery and qualification. A team alias in a workspace is a declaration, not proof that an actor is enrolled or a privacy property is enforced.
|
|
82
82
|
|
|
83
|
-
aweb 1.10.3 supports its legacy setup/lifecycle path but lacks the captured provider-binding interface. **aweb 1.11.0** (OATS >=0.24.2) adds it: `check` qualifies HOME-route operational custody for an input-capable Claude/Codex primary with an explicit private team and `delivery: session`; a strict-Pi print primary reports `needs-configuration` rather than dropping the requirement. Qualification is not account delegation, broker delivery or model consumption.
|
|
83
|
+
aweb 1.10.3 supports its legacy setup/lifecycle path but lacks the captured provider-binding interface. **aweb 1.11.0** (OATS >=0.24.2) adds it (1.11.2, OATS >=0.24.4, is code-identical and declares its fixed reasons and `helperInjection: omit`): `check` qualifies HOME-route operational custody for an input-capable Claude/Codex primary with an explicit private team and `delivery: session`; a strict-Pi print primary reports `needs-configuration` rather than dropping the requirement. Qualification is not account delegation, broker delivery or model consumption.
|
|
84
84
|
|
|
85
85
|
The earlier proposed `reach` ladder is **not an enforced universal field**. In particular, aweb's `team_and_contacts` includes verified same-team senders; the compatibility spellings `contacts-only` and `contacts_only` do not establish owner-only admission. A config command succeeding proves neither inbound/outbound restrictions nor knowledge visibility. See the [identity/membership amendment](design/2026-09-08-expert-assisted-deployment-proposal.md#membership-reach-and-visibility-are-separate) and [messaging boundary](design/2026-09-16-messaging-capability-contract.md).
|
|
86
86
|
|
|
@@ -62,7 +62,7 @@ this policy does not invent new catalog or manifest fields.
|
|
|
62
62
|
|
|
63
63
|
- Listed capabilities: `oats.okf`, `oats.aweb`, `oats.authoring`, `oats.jira`,
|
|
64
64
|
`oats.linear`, `oats.dev`, `oats.knowledge-theory`, `oats.core` and `oats.setup`.
|
|
65
|
-
- **`oats.framework` 1.1.
|
|
65
|
+
- **`oats.framework` 1.1.3** is listed at tag `oats-framework/v1.1.3` in
|
|
66
66
|
`awebai/oats`, payload root `oats-package`. The `oats.core`, `oats.setup` and
|
|
67
67
|
`oats.knowledge-theory` aliases select that distribution; package identity is
|
|
68
68
|
distinct from capability identity. Core supplies operation/soul guidance;
|