@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 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) fail("needs-configuration", "preparation is incomplete; no executable resolution was published", result);
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(), entry = catalog["oats.framework"];
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) { const selected = Object.hasOwn(catalog, id) ? catalog[id] : null; return selected?.url ? { url: selected.url, ref: selector || selected.ref, path: selected.path } : undefined; },
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) throw Object.assign(new Error("selected edition's same-repository package differs from the official acquisition; align the reviewed source and catalog explicitly"), { code: "integrity-drift" });
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.0",
4
+ "version": "1.11.2",
5
5
  "compatibility": {
6
- "oats": ">=0.24.2"
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
- if(!absolute(descriptorFile) || !absolute(stateDir) || !['pi','claude','codex'].includes(runtime) || (model!==null && (typeof model!=='string' || !model.trim()))) wireError('needs-configuration');
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
- candidates.push(...normalizeKnowledgeBindingCandidates({bindings:store.payload.bindings,kind:'workspace-default',origin:item.origin,origins:item.origins,pointer:`/knowledge/stores/${index}/payload/bindings`}));
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
- candidates.push(...normalizeKnowledgeBindingCandidates({bindings:item.value.bindings,kind:item.kind==='adoption'?'import-adoption':'operator',origin:item.origin,origins:item.origins,pointer:'/bindings'}));
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:{code:errorCode(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.1",
4
+ "version": "2.1.2",
5
5
  "compatibility": {
6
- "oats": ">=0.24.0"
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 but never forwarded by the kernel.
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 05:00Z · main `04e930e7` · **OATS v0.24.2 published** · OKF v2.1.1 · oats-framework/v1.1.1 · aweb v1.11.0 · oats-knowledge 8d67eab4
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 | ✅ workspace + seven indexes + six imports · ✅ **0.24.2 published** (onboard verified from the npm tarball) · 🔄 Antares re-run requested · 🔄 five seams → L, 0.24.3 | lead, L, Antares | re-run report; seams PR |
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) | ✅ **repo PUBLIC; PR #1 merged → main 8d67eab4, 25 accepted concepts**, owners = published souls, validator pinned OKF 2.1.1 · 🔄 fresh-reader proof assigned to Juan's side · ⬜ legacy in-soul knowledge decommission | lead, Antares/Juan | fresh-reader + PR-learning proof; then retire `agents/*/soul/knowledge` |
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 (assigned to L, one PR, priority):
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 a valid and a bogus `stores.oats` binding produce byte-identical output. **Root cause (L's trace):** both OKF `normalize` calls refused because the operator request had no OKF runtime settings (`bindings-file`, `state-dir` — absolute host paths, no defaults); the message never said so. Fix = attribute problems by slot/capability and name the missing item; the identical pair is correct output for that input and stays identical. Guidance for the required settings → P (oats-workspace-setup).
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`. Explicit captured selectors
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
@@ -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
- For a kernel build that includes `oats onboard` (check `oats onboard --help`),
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
- This command is **not present in the published 0.24.0/0.24.1 kernels**. It is a
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.1** is listed at tag `oats-framework/v1.1.1` in
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;