@n8r/surface 0.1.0-experimental.2 → 0.1.0-experimental.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/README.md +30 -4
- package/dist/cli.js +174 -22
- package/dist/codec.d.ts +2 -0
- package/dist/codec.js +22 -7
- package/dist/codex.d.ts +1 -1
- package/dist/codex.js +26 -10
- package/dist/contracts.d.ts +108 -2
- package/dist/cyclonedx-schemas/bom-1.7.schema.json +6700 -0
- package/dist/cyclonedx-schemas/cryptography-defs.schema.json +576 -0
- package/dist/cyclonedx-schemas/jsf-0.82.schema.json +240 -0
- package/dist/cyclonedx-schemas/spdx.schema.json +786 -0
- package/dist/cyclonedx-validation.d.ts +1 -0
- package/dist/cyclonedx-validation.js +45 -0
- package/dist/html.js +27 -3
- package/dist/recent.d.ts +9 -0
- package/dist/recent.js +71 -0
- package/dist/revisions.js +42 -0
- package/dist/skill-artifacts-cli.d.ts +1 -0
- package/dist/skill-artifacts-cli.js +13 -0
- package/dist/skill-artifacts.d.ts +15 -0
- package/dist/skill-artifacts.js +184 -0
- package/dist/skill-cyclonedx.d.ts +26 -0
- package/dist/skill-cyclonedx.js +214 -0
- package/dist/skill-presentation.d.ts +10 -0
- package/dist/skill-presentation.js +108 -0
- package/dist/skill-surface-cli.d.ts +1 -0
- package/dist/skill-surface-cli.js +37 -0
- package/dist/skill-surface-store.d.ts +7 -0
- package/dist/skill-surface-store.js +126 -0
- package/dist/surface-bom.d.ts +73 -0
- package/dist/surface-bom.js +139 -0
- package/dist/surface-draft.d.ts +6 -3
- package/dist/surface-draft.js +20 -3
- package/dist/surface-instructions.d.ts +2 -1
- package/dist/surface-instructions.js +10 -1
- package/dist/surface-overview.d.ts +15 -0
- package/dist/surface-overview.js +86 -0
- package/dist/surface-store.d.ts +5 -7
- package/dist/surface-store.js +34 -2
- package/dist/surface-view.d.ts +3 -0
- package/dist/surface-view.js +50 -0
- package/dist/validate.js +298 -0
- package/docs/n8r-surface/cli-bundles.md +72 -0
- package/docs/n8r-surface/command-outcomes.md +151 -4
- package/docs/n8r-surface/contracts.md +10 -0
- package/docs/n8r-surface/critical-path.md +21 -0
- package/docs/n8r-surface/first-use-access.md +143 -0
- package/docs/n8r-surface/first-use-overview.md +262 -0
- package/docs/n8r-surface/getting-started.md +55 -88
- package/docs/n8r-surface/guided-journey-verification.md +137 -0
- package/docs/n8r-surface/implementation-decisions.md +14 -14
- package/docs/n8r-surface/instance-first-research.md +209 -0
- package/docs/n8r-surface/npm-distribution.md +39 -69
- package/docs/n8r-surface/npm-release.md +141 -14
- package/docs/n8r-surface/product-direction.md +327 -0
- package/docs/n8r-surface/researcher-skill.md +105 -51
- package/docs/n8r-surface/security-assurance-strategy.md +267 -0
- package/docs/n8r-surface/skill-bom-verification.md +142 -0
- package/docs/n8r-surface/skill-cyclonedx-verification.md +144 -0
- package/docs/n8r-surface/skill-cyclonedx.md +85 -0
- package/docs/n8r-surface/skill-manual.md +129 -0
- package/docs/n8r-surface/skill-surface-artifacts.md +234 -0
- package/docs/n8r-surface/skill-usefulness-verification.md +135 -0
- package/docs/n8r-surface/skill-workspace-automation.md +359 -0
- package/docs/n8r-surface/surface-description-contract.md +224 -0
- package/docs/n8r-surface/surface-descriptions.md +173 -0
- package/docs/n8r-surface/surface-relationships.md +231 -0
- package/docs/n8r-surface/tester-candidate.md +158 -0
- package/docs/n8r-surface/versioning.md +55 -66
- package/docs/n8r-surface/workspace-human-check.md +101 -0
- package/docs/n8r-surface/workspace-installation.md +68 -0
- package/docs/n8r-surface/workspace-verification.md +350 -0
- package/docs/n8r-surface/workspace.md +83 -0
- package/examples/researcher/README.md +8 -0
- package/examples/researcher/cyclonedx/configured/README.md +7 -0
- package/examples/researcher/cyclonedx/configured/manifest.json +10 -0
- package/examples/researcher/cyclonedx/configured/mapping.json +323 -0
- package/examples/researcher/cyclonedx/configured/surface-records.json +1275 -0
- package/examples/researcher/cyclonedx/configured/surface.cdx.json +79 -0
- package/examples/researcher/descriptions/README.md +29 -0
- package/examples/researcher/descriptions/bound-r1.json +141 -0
- package/examples/researcher/descriptions/bound-r2.json +153 -0
- package/examples/researcher/descriptions/capability.json +167 -0
- package/examples/researcher/descriptions/research-r1.json +153 -0
- package/examples/researcher/descriptions/research-r2.json +157 -0
- package/examples/researcher/descriptions/sources.md +24 -0
- package/examples/researcher/relationships/README.md +34 -0
- package/examples/researcher/relationships/bound-r1.json +346 -0
- package/examples/researcher/relationships/bound-r2.json +358 -0
- package/examples/researcher/relationships/capability.json +325 -0
- package/examples/researcher/relationships/config/assistant.env.example +4 -0
- package/examples/researcher/relationships/research-r1.json +300 -0
- package/examples/researcher/relationships/research-r2.json +304 -0
- package/examples/researcher/relationships/sources.md +38 -0
- package/examples/researcher/surface-bom/README.md +17 -0
- package/examples/researcher/surface-bom/initial.cdx.json +188 -0
- package/package.json +1 -1
- package/plugins/n8r-surface/.codex-plugin/plugin.json +2 -2
- package/plugins/n8r-surface/LICENSING.md +3 -0
- package/plugins/n8r-surface/plugin.json +2 -2
- package/plugins/n8r-surface/skills/research/SKILL.md +79 -38
- package/plugins/n8r-surface/skills/research/references/artifacts.md +152 -0
- package/plugins/n8r-surface/skills/research/references/cyclonedx.md +207 -0
- package/plugins/n8r-surface/skills/research/references/discovery-map.md +20 -2
- package/plugins/n8r-surface/skills/research/references/examples/bound-r1.json +346 -0
- package/plugins/n8r-surface/skills/research/references/examples/capability.json +325 -0
- package/plugins/n8r-surface/skills/research/references/examples/research-r1.json +300 -0
- package/plugins/n8r-surface/skills/research/references/examples/surface.cdx.json +188 -0
- package/plugins/n8r-surface/skills/research/references/first-use-access.md +97 -0
- package/plugins/n8r-surface/skills/research/references/first-use-overview.md +101 -0
- package/plugins/n8r-surface/skills/research/references/instance-first.md +125 -0
- package/plugins/n8r-surface/skills/research/references/working-notes.md +6 -1
- package/plugins/n8r-surface/skills/research/references/workspace.md +89 -0
- package/plugins/n8r-surface/skills/research/scripts/THIRD-PARTY-NOTICES.txt +273 -0
- package/plugins/n8r-surface/skills/research/scripts/artifacts.mjs +8348 -0
- package/plugins/n8r-surface/skills/research/scripts/cyclonedx.mjs +28113 -0
- package/plugins/n8r-surface/skills/research/scripts/surface.mjs +27800 -0
- package/plugins/n8r-surface/skills/research/scripts/workspace.mjs +120 -0
package/dist/validate.js
CHANGED
|
@@ -116,7 +116,22 @@ function references(ids, available, p) {
|
|
|
116
116
|
if (!available.has(item))
|
|
117
117
|
fail(p, `unresolved reference ${item}`);
|
|
118
118
|
}
|
|
119
|
+
// Inspect only a data descriptor; never invoke an untrusted getter to dispatch.
|
|
120
|
+
function isDescription(input) {
|
|
121
|
+
return input !== null && typeof input === 'object' &&
|
|
122
|
+
['n8r.surface/0.2', 'n8r.surface/0.3'].includes(Object.getOwnPropertyDescriptor(input, 'contract')?.value);
|
|
123
|
+
}
|
|
119
124
|
export function validateArtefact(input) {
|
|
125
|
+
try {
|
|
126
|
+
return validate(input);
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
if (isDescription(input))
|
|
130
|
+
fail('$description', 'invalid structure, identity, state or reference');
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function validate(input) {
|
|
120
135
|
canonical(input); // checks portable values, depth and logical size before field access
|
|
121
136
|
const data = object(input, '$');
|
|
122
137
|
if (data.contract === 'n8r.research/0.1') {
|
|
@@ -154,6 +169,12 @@ export function validateArtefact(input) {
|
|
|
154
169
|
references([e.to], nodes, e.id);
|
|
155
170
|
}
|
|
156
171
|
}
|
|
172
|
+
else if (data.contract === 'n8r.surface/0.2') {
|
|
173
|
+
validateDescription(input);
|
|
174
|
+
}
|
|
175
|
+
else if (data.contract === 'n8r.surface/0.3') {
|
|
176
|
+
validateGraph(input);
|
|
177
|
+
}
|
|
157
178
|
else
|
|
158
179
|
fail('$.contract', 'unsupported contract/version');
|
|
159
180
|
const a = input;
|
|
@@ -163,9 +184,23 @@ export function validateArtefact(input) {
|
|
|
163
184
|
}
|
|
164
185
|
/** Explicit offline bundle verification. Missing external context is an error here. */
|
|
165
186
|
export function verifyReferences(input, context) {
|
|
187
|
+
try {
|
|
188
|
+
verifyContext(input, context);
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (isDescription(input))
|
|
192
|
+
fail('$description', 'invalid or unavailable evidence, capability or exclusive selection');
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function verifyContext(input, context) {
|
|
166
197
|
const a = validateArtefact(input);
|
|
167
198
|
const records = context.map(validateArtefact);
|
|
168
199
|
unique(records.map(r => JSON.stringify([r.id, r.revision.id])), '$context');
|
|
200
|
+
if (a.contract === 'n8r.surface/0.2' || a.contract === 'n8r.surface/0.3') {
|
|
201
|
+
verifyDescriptionContext(a, records);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
169
204
|
if (a.contract !== 'n8r.surface/0.1')
|
|
170
205
|
return;
|
|
171
206
|
for (const ref of a.records) {
|
|
@@ -182,3 +217,266 @@ export function verifyReferences(input, context) {
|
|
|
182
217
|
}
|
|
183
218
|
}
|
|
184
219
|
}
|
|
220
|
+
const basis = shape({ record: id, revision: token, items: array(id, 1) });
|
|
221
|
+
const descriptionType = shape({ vocabulary: id, term: text });
|
|
222
|
+
const descriptionCommon = {
|
|
223
|
+
...common, contract: oneOf('n8r.surface/0.2'), extensions: shape({}),
|
|
224
|
+
records: array(ref), vocabularies: array(shape({ id, version: token })),
|
|
225
|
+
nodes: array(shape({ id, label: text, type: descriptionType })),
|
|
226
|
+
coverage: array(text), gaps: array(text)
|
|
227
|
+
};
|
|
228
|
+
const capabilityShape = shape({
|
|
229
|
+
...descriptionCommon, kind: oneOf('capability'),
|
|
230
|
+
capabilities: array(shape({ id, from: id, to: id, type: descriptionType,
|
|
231
|
+
state: oneOf('possible', 'unknown', 'omitted', 'redacted', 'absent', 'out-of-scope', 'conflicting'),
|
|
232
|
+
basis: array(basis), conditions: array(text), note: text })),
|
|
233
|
+
exclusiveGroups: array(shape({ id, members: array(id, 2) }))
|
|
234
|
+
});
|
|
235
|
+
const boundShape = shape({
|
|
236
|
+
...descriptionCommon, kind: oneOf('bound'), capability: ref,
|
|
237
|
+
instance: shape({ id, label: text }),
|
|
238
|
+
bindings: array(shape({ id, capability: id,
|
|
239
|
+
state: oneOf('configured', 'unknown', 'omitted', 'redacted', 'out-of-scope', 'conflicting'),
|
|
240
|
+
claims: array(shape({ target: id, basis: array(basis, 1) })), decisions: array(basis), note: text }))
|
|
241
|
+
});
|
|
242
|
+
function descriptionBases(a) {
|
|
243
|
+
return a.kind === 'capability' ? a.capabilities.flatMap(c => c.basis) :
|
|
244
|
+
a.bindings.flatMap(b => [...b.decisions, ...b.claims.flatMap(c => c.basis)]);
|
|
245
|
+
}
|
|
246
|
+
function validateDescription(input) {
|
|
247
|
+
const data = object(input, '$');
|
|
248
|
+
(data.kind === 'capability' ? capabilityShape : boundShape)(input, '$');
|
|
249
|
+
const a = input;
|
|
250
|
+
const entries = a.kind === 'capability' ? [...a.capabilities, ...a.exclusiveGroups] : a.bindings;
|
|
251
|
+
unique([a.id, ...a.nodes.map(n => n.id), ...entries.map(e => e.id), ...(a.kind === 'bound' ? [a.instance.id] : [])], '$');
|
|
252
|
+
const nodes = new Set(a.nodes.map(n => n.id));
|
|
253
|
+
const vocab = unique(a.vocabularies.map(v => v.id), '$.vocabularies');
|
|
254
|
+
const records = unique(a.records.map(r => JSON.stringify([r.id, r.revision])), '$.records');
|
|
255
|
+
for (const n of a.nodes)
|
|
256
|
+
references([n.type.vocabulary], vocab, '$.nodes');
|
|
257
|
+
for (const b of descriptionBases(a)) {
|
|
258
|
+
references([JSON.stringify([b.record, b.revision])], records, '$.basis');
|
|
259
|
+
unique(b.items, '$.basis.items');
|
|
260
|
+
}
|
|
261
|
+
if (a.kind === 'capability') {
|
|
262
|
+
for (const c of a.capabilities) {
|
|
263
|
+
references([c.from], nodes, '$.capabilities.from');
|
|
264
|
+
references([c.to], nodes, '$.capabilities.to');
|
|
265
|
+
references([c.type.vocabulary], vocab, '$.capabilities.type');
|
|
266
|
+
if (['possible', 'absent', 'conflicting'].includes(c.state) && !c.basis.length)
|
|
267
|
+
fail('$', 'evidence required');
|
|
268
|
+
}
|
|
269
|
+
for (const group of a.exclusiveGroups)
|
|
270
|
+
references(group.members, new Set(a.capabilities.map(c => c.id)), '$.exclusiveGroups');
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
unique(a.bindings.map(b => b.capability), '$.bindings.capability');
|
|
274
|
+
for (const b of a.bindings) {
|
|
275
|
+
const count = b.claims.length;
|
|
276
|
+
if (b.state === 'configured' ? count !== 1 : b.state === 'conflicting' ? count < 2 : count !== 0)
|
|
277
|
+
fail('$', 'state/claims mismatch');
|
|
278
|
+
references(b.claims.map(c => c.target), nodes, '$.bindings.targets');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function verifyDescriptionContext(a, context) {
|
|
283
|
+
const research = a.records.map(ref => {
|
|
284
|
+
const r = context.find(r => r.id === ref.id && r.revision.id === ref.revision);
|
|
285
|
+
if (!r || r.contract !== 'n8r.research/0.1' || digest(r) !== ref.digest)
|
|
286
|
+
fail('$', 'research revision/digest unavailable');
|
|
287
|
+
return r;
|
|
288
|
+
});
|
|
289
|
+
const items = (bases, decisions = false) => bases.flatMap(b => {
|
|
290
|
+
const r = research.find(r => r.id === b.record && r.revision.id === b.revision);
|
|
291
|
+
return b.items.map(id => {
|
|
292
|
+
const item = (decisions ? r.decisions : r.findings).find(f => f.id === id);
|
|
293
|
+
if (!item)
|
|
294
|
+
fail('$', 'wrong or missing evidence item kind');
|
|
295
|
+
return item;
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
const findings = (bases) => items(bases);
|
|
299
|
+
if (a.contract === 'n8r.surface/0.3')
|
|
300
|
+
verifyRelationshipDetails(a, research);
|
|
301
|
+
const verifySupport = (c) => {
|
|
302
|
+
const fs = findings(c.basis);
|
|
303
|
+
if (fs.some(f => f.subject !== c.from || f.property !== 'supports'))
|
|
304
|
+
fail('$', 'capability evidence subject/property mismatch');
|
|
305
|
+
if (c.state === 'possible' && (!fs.length || fs.some(f => f.state !== 'known' || f.claims.some(cl => cl.value !== c.to))))
|
|
306
|
+
fail('$', 'possible requires supported target evidence');
|
|
307
|
+
if (c.state === 'absent' && (!fs.length || fs.some(f => f.state !== 'absent' || f.claims.some(cl => cl.value !== c.to))))
|
|
308
|
+
fail('$', 'absent requires scoped absence evidence');
|
|
309
|
+
if (c.state === 'conflicting' && !fs.some(f => f.state === 'conflicting'))
|
|
310
|
+
fail('$', 'conflicting requires conflicting finding');
|
|
311
|
+
};
|
|
312
|
+
if (a.kind === 'capability') {
|
|
313
|
+
for (const c of capabilityEdges(a))
|
|
314
|
+
verifySupport(c);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const ref = a.capability;
|
|
318
|
+
const c = context.find(c => c.id === ref.id && c.revision.id === ref.revision);
|
|
319
|
+
if (!c || (c.contract !== 'n8r.surface/0.2' && c.contract !== 'n8r.surface/0.3') || c.contract !== a.contract || c.kind !== 'capability' || digest(c) !== ref.digest)
|
|
320
|
+
fail('$', 'capability revision/digest unavailable');
|
|
321
|
+
verifyDescriptionContext(c, context);
|
|
322
|
+
// A shared node ID denotes the same component; an instance is a distinct identity.
|
|
323
|
+
if ([c.id, ...c.nodes.map(n => n.id), ...capabilityEdges(c).map(e => e.id)].includes(a.instance.id))
|
|
324
|
+
fail('$', 'instance identity reused');
|
|
325
|
+
for (const n of a.nodes) {
|
|
326
|
+
const old = c.nodes.find(x => x.id === n.id);
|
|
327
|
+
if (old && canonical(old.type) !== canonical(n.type))
|
|
328
|
+
fail('$', 'shared component type changed');
|
|
329
|
+
}
|
|
330
|
+
if (a.contract === 'n8r.surface/0.3') {
|
|
331
|
+
for (const edge of a.relationships) {
|
|
332
|
+
const base = capabilityEdges(c).find(x => x.id === edge.id);
|
|
333
|
+
if (!base || edge.from !== base.from || edge.to !== base.to || canonical(edge.type) !== canonical(base.type))
|
|
334
|
+
fail('$', 'relationship differs from pinned Capability');
|
|
335
|
+
verifySupport({ ...edge, state: base.state });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
for (const b of boundBindings(a)) {
|
|
339
|
+
const capability = capabilityEdges(c).find(c => c.id === b.capability);
|
|
340
|
+
if (!capability)
|
|
341
|
+
fail('$', 'unknown capability');
|
|
342
|
+
if (b.state === 'configured' && capability.state !== 'possible')
|
|
343
|
+
fail('$', 'configured requires established possibility');
|
|
344
|
+
items(b.decisions, true);
|
|
345
|
+
const targets = new Set(b.claims.map(cl => cl.target));
|
|
346
|
+
if (b.claims.length) {
|
|
347
|
+
// Do not allow an author to hide contrary evidence by omitting a basis item.
|
|
348
|
+
// Only this Bound snapshot's records apply; the pinned Capability may use
|
|
349
|
+
// older research revisions which must not override the current binding.
|
|
350
|
+
const matching = research.flatMap(r => r.findings).filter(f => f.subject === a.instance.id && f.property === b.capability &&
|
|
351
|
+
['known', 'conflicting'].includes(f.state));
|
|
352
|
+
for (const f of matching) {
|
|
353
|
+
if (f.claims.some(cl => typeof cl.value !== 'string' || !targets.has(cl.value)) ||
|
|
354
|
+
(b.state === 'configured' && f.state === 'conflicting'))
|
|
355
|
+
fail('$', 'omitted contradictory configuration evidence');
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
for (const claim of b.claims) {
|
|
359
|
+
const fs = findings(claim.basis);
|
|
360
|
+
for (const f of fs) {
|
|
361
|
+
if (f.subject !== a.instance.id || f.property !== b.capability ||
|
|
362
|
+
!['known', 'conflicting'].includes(f.state) ||
|
|
363
|
+
!f.claims.some(cl => cl.value === claim.target) ||
|
|
364
|
+
f.claims.some(cl => typeof cl.value !== 'string' || !targets.has(cl.value)))
|
|
365
|
+
fail('$', 'binding evidence mismatch or suppressed conflict');
|
|
366
|
+
if (b.state === 'configured' && f.state !== 'known')
|
|
367
|
+
fail('$', 'conflict cannot establish configured');
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
for (const group of c.exclusiveGroups) {
|
|
372
|
+
if (boundBindings(a).filter(b => b.state === 'configured' && group.members.includes(b.capability)).length > 1)
|
|
373
|
+
fail('$', 'exclusive capabilities configured together');
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function capabilityEdges(a) {
|
|
377
|
+
return a.contract === 'n8r.surface/0.3' ? a.relationships : a.capabilities;
|
|
378
|
+
}
|
|
379
|
+
function boundBindings(a) {
|
|
380
|
+
return a.contract === 'n8r.surface/0.3' ? a.relationships.map(e => ({ id: e.id, capability: e.id, ...e.binding })) : a.bindings;
|
|
381
|
+
}
|
|
382
|
+
const configLocation = (v, p) => {
|
|
383
|
+
text(v, p);
|
|
384
|
+
const s = v;
|
|
385
|
+
if (s.startsWith('/') || /[\\:?#%]/.test(s) || s.split('/').some(part => !part || part === '.' || part === '..'))
|
|
386
|
+
fail(p, 'requires safe relative documentation location');
|
|
387
|
+
};
|
|
388
|
+
const configReference = shape({ owner: id, mechanism: oneOf('environment-variable', 'file-key', 'argument', 'admin-setting', 'code'), key: text, location: configLocation });
|
|
389
|
+
const detailShape = (check) => (v, p) => {
|
|
390
|
+
shape({ state: oneOf('known', 'unknown', 'omitted', 'redacted', 'absent', 'out-of-scope', 'conflicting'),
|
|
391
|
+
claims: array(shape({ value: check, basis: array(basis, 1) })), note: text })(v, p);
|
|
392
|
+
const d = v;
|
|
393
|
+
const count = d.claims.length;
|
|
394
|
+
if (d.state === 'known' || d.state === 'absent') {
|
|
395
|
+
if (count !== 1)
|
|
396
|
+
fail(p, 'known/absent requires one evidenced claim');
|
|
397
|
+
}
|
|
398
|
+
else if (d.state === 'conflicting') {
|
|
399
|
+
if (count < 2 || new Set(d.claims.map(c => canonical(c))).size !== count)
|
|
400
|
+
fail(p, 'conflicting requires distinct evidenced claims');
|
|
401
|
+
}
|
|
402
|
+
else if (count)
|
|
403
|
+
fail(p, 'non-value state cannot carry claims');
|
|
404
|
+
};
|
|
405
|
+
const sharedRelationship = { id, from: id, to: id, type: descriptionType,
|
|
406
|
+
basis: array(basis), conditions: array(text), note: text,
|
|
407
|
+
purpose: detailShape(text), integration: detailShape(text), configuration: detailShape(array(configReference)) };
|
|
408
|
+
const bindingAnnotation = shape({
|
|
409
|
+
state: oneOf('configured', 'unknown', 'omitted', 'redacted', 'out-of-scope', 'conflicting'),
|
|
410
|
+
claims: array(shape({ target: id, basis: array(basis, 1) })), decisions: array(basis), note: text
|
|
411
|
+
});
|
|
412
|
+
const graphCommon = { ...descriptionCommon, contract: oneOf('n8r.surface/0.3') };
|
|
413
|
+
const capabilityGraphShape = shape({ ...graphCommon, kind: oneOf('capability'),
|
|
414
|
+
relationships: array(shape({ ...sharedRelationship, state: oneOf('possible', 'unknown', 'omitted', 'redacted', 'absent', 'out-of-scope', 'conflicting') })),
|
|
415
|
+
exclusiveGroups: array(shape({ id, members: array(id, 2) })) });
|
|
416
|
+
const boundGraphShape = shape({ ...graphCommon, kind: oneOf('bound'), capability: ref, instance: shape({ id, label: text }),
|
|
417
|
+
relationships: array(shape({ ...sharedRelationship, binding: bindingAnnotation })) });
|
|
418
|
+
const detailNames = ['purpose', 'integration', 'configuration'];
|
|
419
|
+
function validateGraph(input) {
|
|
420
|
+
const data = object(input, '$');
|
|
421
|
+
(data.kind === 'capability' ? capabilityGraphShape : boundGraphShape)(input, '$');
|
|
422
|
+
const a = input;
|
|
423
|
+
// Reuse the bounded 0.2 local state/identity checks without changing the input
|
|
424
|
+
// or the meaning/version of either serialized contract.
|
|
425
|
+
const legacy = a.kind === 'capability' ? {
|
|
426
|
+
...a, contract: 'n8r.surface/0.2', capabilities: a.relationships.map(e => {
|
|
427
|
+
const { purpose, integration, configuration, ...core } = e;
|
|
428
|
+
return core;
|
|
429
|
+
})
|
|
430
|
+
} : { ...a, contract: 'n8r.surface/0.2', bindings: boundBindings(a) };
|
|
431
|
+
delete legacy.relationships;
|
|
432
|
+
validateDescription(legacy);
|
|
433
|
+
const nodes = new Set(a.nodes.map(n => n.id));
|
|
434
|
+
const vocab = new Set(a.vocabularies.map(v => v.id));
|
|
435
|
+
const records = new Set(a.records.map(r => JSON.stringify([r.id, r.revision])));
|
|
436
|
+
for (const e of a.relationships) {
|
|
437
|
+
references([e.from], nodes, '$.relationships.from');
|
|
438
|
+
references([e.to], nodes, '$.relationships.to');
|
|
439
|
+
references([e.type.vocabulary], vocab, '$.relationships.type');
|
|
440
|
+
const bases = [...e.basis, ...detailNames.flatMap(name => e[name].claims.flatMap(c => c.basis))];
|
|
441
|
+
for (const b of bases) {
|
|
442
|
+
references([JSON.stringify([b.record, b.revision])], records, '$.relationships.basis');
|
|
443
|
+
unique(b.items, '$.relationships.items');
|
|
444
|
+
}
|
|
445
|
+
for (const claim of e.configuration.claims)
|
|
446
|
+
for (const config of claim.value)
|
|
447
|
+
references([config.owner], nodes, '$.configuration.owner');
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function verifyRelationshipDetails(a, research) {
|
|
451
|
+
for (const e of a.relationships)
|
|
452
|
+
for (const property of detailNames) {
|
|
453
|
+
const detail = e[property];
|
|
454
|
+
const values = new Set(detail.claims.map(c => canonical(c.value)));
|
|
455
|
+
const assertions = new Set();
|
|
456
|
+
for (const claim of detail.claims)
|
|
457
|
+
for (const b of claim.basis) {
|
|
458
|
+
const r = research.find(r => r.id === b.record && r.revision.id === b.revision);
|
|
459
|
+
for (const id of b.items) {
|
|
460
|
+
const f = r.findings.find(f => f.id === id);
|
|
461
|
+
if (!f || f.subject !== e.id || f.property !== property ||
|
|
462
|
+
(detail.state === 'known' ? f.state !== 'known' : detail.state === 'absent' ? f.state !== 'absent' : !['known', 'absent', 'conflicting'].includes(f.state)) ||
|
|
463
|
+
!f.claims.some(c => canonical(c.value) === canonical(claim.value)) ||
|
|
464
|
+
f.claims.some(c => !values.has(canonical(c.value))))
|
|
465
|
+
fail('$', 'relationship detail evidence mismatch');
|
|
466
|
+
assertions.add(f.state);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (detail.state === 'conflicting' && values.size < 2 &&
|
|
470
|
+
!(assertions.has('known') && assertions.has('absent')))
|
|
471
|
+
fail('$', 'agreeing evidence does not establish conflict');
|
|
472
|
+
if (detail.claims.length) {
|
|
473
|
+
const matching = research.flatMap(r => r.findings).filter(f => f.subject === e.id && f.property === property);
|
|
474
|
+
for (const f of matching) {
|
|
475
|
+
if (f.claims.some(c => !values.has(canonical(c.value))) ||
|
|
476
|
+
(detail.state === 'known' && ['absent', 'conflicting'].includes(f.state)) ||
|
|
477
|
+
(detail.state === 'absent' && ['known', 'conflicting'].includes(f.state)))
|
|
478
|
+
fail('$', 'omitted contradictory relationship detail');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# CLI Capability and Bound bundles
|
|
2
|
+
|
|
3
|
+
The CLI candidate uses the same checked artifact machinery as Researcher skill
|
|
4
|
+
0.1.11. New research can stage `n8r.surface/0.3` Capability or Bound bundles.
|
|
5
|
+
`/save surface` saves the preview's brief, research HTML, Surface JSON, pinned
|
|
6
|
+
dependencies and manifest in the next numbered directory. Reopening either
|
|
7
|
+
`research.html` or `surface.json` reads and checks the complete bundle.
|
|
8
|
+
|
|
9
|
+
Existing `n8r.surface/0.1` pairs remain readable and can be revised in their
|
|
10
|
+
original format. There is no automatic migration. A Capability and a Bound
|
|
11
|
+
Surface are separate identities; continue a saved description in its original
|
|
12
|
+
kind. Start a new discovery for a different kind. The CLI can also reopen a
|
|
13
|
+
standalone skill bundle. Such a bundle has no CLI runner receipt; historical
|
|
14
|
+
source references are rechecked rather than assigned invented runner provenance.
|
|
15
|
+
|
|
16
|
+
The runner receives bundled schema examples as synthetic shapes, not findings.
|
|
17
|
+
It computes canonical artifact hashes through a read-only host tool, then stages
|
|
18
|
+
a complete bundle. The host checks current and dependency research sources
|
|
19
|
+
against actual reads or saved evidence. Validation does not establish research
|
|
20
|
+
accuracy, complete secret detection, runtime use or consumer compatibility.
|
|
21
|
+
Use permitted sources or sanitized configuration summaries; do not supply secrets.
|
|
22
|
+
|
|
23
|
+
## Human verification
|
|
24
|
+
|
|
25
|
+
Maintainer preparation: install the candidate in the tester's active Node
|
|
26
|
+
installation, confirm Codex sign-in/model access and vault setup, and open the
|
|
27
|
+
terminal in the existing prepared Lumen source directory. Do not ask the tester
|
|
28
|
+
to build packages or find paths. The checks below send that source to OpenAI
|
|
29
|
+
after confirmation. See [using the command](getting-started.md).
|
|
30
|
+
|
|
31
|
+
- [ ] Run `n8r-surface`. At `You>`, enter: “Research the AI connections in this
|
|
32
|
+
guide. Keep unknown configuration and runtime use explicit. My decision is to
|
|
33
|
+
defer runtime verification.” Confirm research. Expect findings based on the
|
|
34
|
+
guide and the recorded decision. Ask a follow-up question in your own words;
|
|
35
|
+
the conversation should let you explore freely.
|
|
36
|
+
- [ ] Type `save`. Open the brief or HTML at the returned path. Expect a useful
|
|
37
|
+
explanation of the connections, source evidence, unknowns and your decision.
|
|
38
|
+
Type `exit` when you want to leave. No separate Surface-preparation command is
|
|
39
|
+
required; saving does not end the conversation.
|
|
40
|
+
- [ ] Run `n8r-surface` again and select the saved title by its number. Expect
|
|
41
|
+
the previous research and decision to return without copying a file path.
|
|
42
|
+
Say: “Keep my runtime-verification deferral. Add my decision to defer external
|
|
43
|
+
OIDC investigation because model-service configuration is the priority.”
|
|
44
|
+
Then type `save`. Expect a new saved result with both decisions; the previous
|
|
45
|
+
result remains available and unchanged. Type `exit` to leave (exit 0).
|
|
46
|
+
- [ ] Report **Pass / Fail / Blocked** and any point where the experience was
|
|
47
|
+
confusing or the result was wrong, with the saved links. Wording and filenames
|
|
48
|
+
can vary. Missing saves, lost decisions or overwritten files fail. Missing
|
|
49
|
+
installation or sign-in is blocked. Keep the saved work; no cleanup is needed.
|
|
50
|
+
|
|
51
|
+
Maintainer evidence checks cover exact parent digests, preserved source/settings
|
|
52
|
+
bytes, dependency validation and the local recent-work index. They do not replace
|
|
53
|
+
the user's judgment of the conversation and result.
|
|
54
|
+
|
|
55
|
+
## Maintainer evidence
|
|
56
|
+
|
|
57
|
+
At baseline `5eed2ac`, the installed CLI completed the first interaction but
|
|
58
|
+
saved `n8r.surface/0.1`. Candidate regression checks cover shared-helper readback,
|
|
59
|
+
fresh-store successors, dependency corruption, lost decisions, standalone skill
|
|
60
|
+
reopening and legacy pairs. Real-run evidence is recorded with the implementing
|
|
61
|
+
PR; automated checks are not human acceptance or publication evidence.
|
|
62
|
+
|
|
63
|
+
On 8 November 2026 an installed development tarball based on `5eed2ac` (dirty)
|
|
64
|
+
completed the exact two-session task above with Node 26.13.0, Codex 0.166.0 and
|
|
65
|
+
`gpt-6-astra` using ChatGPT sign-in. Both processes exited 0. Independent helper
|
|
66
|
+
readback verified Capability kind, exact research/Surface parents, retained
|
|
67
|
+
component IDs and runtime deferral, added OIDC deferral and pinned dependencies.
|
|
68
|
+
Source, settings and every predecessor file remained unchanged. Tarball SHA-256:
|
|
69
|
+
`0a4cc51d39327dfab67e186516f9eef1a01efcb1369c7ae35a9cb843e62c458f`.
|
|
70
|
+
This is an agent rehearsal, not a new human acceptance or registry release.
|
|
71
|
+
Subsequent changes broadened dependency-source checks and preserved optional
|
|
72
|
+
runner/note receipts; final candidate verification must identify its own bytes.
|
|
@@ -24,6 +24,54 @@ user: define a real task, attempt it through the product, observe the gap, devel
|
|
|
24
24
|
what is missing, then repeat the same task. It does not publish private Lab
|
|
25
25
|
implementation material or require that application to be running.
|
|
26
26
|
|
|
27
|
+
Candidate skill 0.1.10 adds standalone Capability/Bound artifact production and
|
|
28
|
+
revision, without adding a CLI command. See the [task, evidence and human
|
|
29
|
+
check](skill-surface-artifacts.md). Installed rehearsal and human acceptance remain
|
|
30
|
+
distinct from contract checks.
|
|
31
|
+
|
|
32
|
+
## Open, research, save and return (#17)
|
|
33
|
+
|
|
34
|
+
Given a configured vault and signed-in Codex, running `n8r-surface` in a terminal
|
|
35
|
+
opens a research prompt and lists saved work by title. Type the investigation;
|
|
36
|
+
the current folder is the default source. Optional `/source PATH` changes it. Confirm source
|
|
37
|
+
transmission once, then converse normally. Type `save` to save the result and
|
|
38
|
+
`exit` to leave. Running `n8r-surface` again offers the saved title; selecting its
|
|
39
|
+
number reopens it with the remembered source and model. Save a successor without
|
|
40
|
+
replacing earlier files. Missing/moved sources require a new source selection.
|
|
41
|
+
|
|
42
|
+
Plain `save`, `save the result` and guided `/save` prepare a checked Surface if
|
|
43
|
+
needed and persist it only after validation. `/save note` saves Markdown.
|
|
44
|
+
Explicit `discover`, `reopen`, their options and legacy `/save` note behavior
|
|
45
|
+
remain available. Noninteractive startup retains the read-only overview.
|
|
46
|
+
A local recent-work index beside settings remembers source/model/output paths;
|
|
47
|
+
it is not part of portable Surface data. Index failure must not conceal an
|
|
48
|
+
otherwise successful artifact save. No saved outputs are removed by this flow.
|
|
49
|
+
|
|
50
|
+
Installed .3 baseline prints configuration and exits after vault readiness;
|
|
51
|
+
it offers no research prompt or remembered-work selection. Verification will
|
|
52
|
+
exercise bare startup, plain-language research/save, exit and fresh startup
|
|
53
|
+
selection through the installed command, then independently check the files.
|
|
54
|
+
|
|
55
|
+
## CLI Capability/Bound increment (#17)
|
|
56
|
+
|
|
57
|
+
Given an installed candidate, a configured vault, Codex 0.166.0 signed in with
|
|
58
|
+
ChatGPT and permitted local sources, ask `discover SOURCE` to prepare a Capability
|
|
59
|
+
or Bound Surface. Enter `/save surface`, exit, then run
|
|
60
|
+
`reopen SAVED/surface.json --source SOURCE` and request a successor. The same
|
|
61
|
+
commands remain public; there is no new subcommand or automatic migration.
|
|
62
|
+
|
|
63
|
+
Expected: a preview distinguishes possibilities from configured bindings;
|
|
64
|
+
explicit save creates brief, HTML, JSON, dependencies and a checked manifest in
|
|
65
|
+
the next numbered directory. Fresh reopening retains exact dependencies,
|
|
66
|
+
identities, human decisions and parent references. Read the files independently
|
|
67
|
+
and compare predecessor/source bytes. Existing 0.1 pairs remain readable and
|
|
68
|
+
continue in their original format. No runtime-use or Labs compatibility follows.
|
|
69
|
+
|
|
70
|
+
The installed baseline at `5eed2ac` produced an older 0.1 definition for the
|
|
71
|
+
Capability request. The installed development candidate completed save/reopen/revise with independent
|
|
72
|
+
readback; see [the task and bounded evidence](cli-bundles.md). Final candidate
|
|
73
|
+
bytes and human acceptance remain separate from that rehearsal.
|
|
74
|
+
|
|
27
75
|
## Grow the specification with working verification
|
|
28
76
|
|
|
29
77
|
Work one useful behavior at a time, including behavior that already exists. Add a
|
|
@@ -55,10 +103,12 @@ Current progress, with evidence defining the verified scope and revision:
|
|
|
55
103
|
|
|
56
104
|
| User or consumer intent | Current progress | Verification / next increment |
|
|
57
105
|
| --- | --- | --- |
|
|
106
|
+
| Create, repeat and reopen a standalone workspace through the skill. | Specified in accepted #41; helper and skill candidate 0.1.9 implemented. Installed-product scenarios and readback passed; overall PM acceptance pending. No new CLI command. | [Workspace contract](workspace.md), [human tasks](workspace-human-check.md), [evidence](workspace-verification.md). |
|
|
58
107
|
| Create a security-focused skeleton and reconcile a later declaration change. | Skeleton Surface v0.1 guidance in installed skill 0.1.8; bounded agent brief/successor rehearsal and readback passed. Human acceptance pending; no new CLI command or Lab automation. | [SKELETON-01/02 prompts and readback](researcher-skeleton-check.md); [versioned results and remaining acceptance](researcher-skill-verification.md#skeleton-surface-018). |
|
|
59
108
|
| Read saved research and selected definitions through the library, retaining identities, findings and references. | Implemented and verified for the experimental library profile; no command claim. | [CONTRACT-01 and repeatable fixture/package checks](verification.md#contract-01-retain-useful-attributable-research-and-selected-definitions). |
|
|
60
109
|
| Run `n8r-surface` alone and see settings, saved artefacts and known process status. | Specified, implemented and verified for the read-only overview and guidance question; setup/conversation unavailable. | [STARTUP-01 installed evidence at `b90358a`](startup-verification.md). Historical read-only baseline; setup supersedes its guidance and exit behavior. |
|
|
61
110
|
| Choose a local vault once and reuse it from another source directory. | Setup-only implementation; installed rehearsal and human acceptance tracked separately. | [SETUP-01 evidence and tasks](vault-setup-verification.md). Research and continuation remain pending. |
|
|
111
|
+
| Begin with a description and establish source access progressively. | Candidate 0.1.16 guidance; baseline rehearsal recorded, web acceptance tracked separately. No new CLI command or access adapter. | [First-use tasks and evidence](first-use-access.md). |
|
|
62
112
|
| Research supplied sources through the installed skill and save working notes. | Implemented; bounded Lumen and LibreChat agent rehearsals have saved/read back notes. Actual human input survived fresh-agent continuation; Work UI acceptance remains pending. | [RESEARCH-01 evidence and limits](researcher-skill-verification.md). No structured-output or standalone-runner claim. |
|
|
63
113
|
| Offer to retain an initial partial graph when saving is undecided. | Implemented in skill 0.1.7; acceptance and evidence are tracked separately. | [Optional save offer](researcher-skill-verification.md#optional-save-offer-017). |
|
|
64
114
|
| Resume from saved recommendations and retain useful next investigations. | Implemented in skill 0.1.6; installed fresh-agent save/continue and independent readback passed. Human acceptance remains pending. | [Recommendations evidence](researcher-skill-verification.md#saved-recommendations-016) and [prepared interaction](researcher-prepared-check.md). |
|
|
@@ -545,8 +595,8 @@ validation and read-only release mapping verification. Synthetic Git repositorie
|
|
|
545
595
|
exercise missing/wrong tags, dirty source, altered archives and release metadata;
|
|
546
596
|
they do not establish an authenticated release or registry provenance. The public
|
|
547
597
|
CLI inventory is unchanged; `check:version` and `check:release` are maintainer npm
|
|
548
|
-
scripts.
|
|
549
|
-
and
|
|
598
|
+
scripts. The subsequent [scoped release](npm-release.md#verified-scoped-release)
|
|
599
|
+
adds authenticated release and registry evidence beyond those synthetic checks.
|
|
550
600
|
|
|
551
601
|
## RELEASE-01: prepare and publish the first dual release
|
|
552
602
|
|
|
@@ -560,5 +610,102 @@ The manual workflow prepares retained archives before publication and verifies
|
|
|
560
610
|
existing registry bytes before staging only absent versions for maintainer 2FA approval. Tests cover altered
|
|
561
611
|
bytes, wrong registry identity/integrity, partial-publication recovery and network
|
|
562
612
|
failure. These use synthetic registry boundaries, not authenticated publication.
|
|
563
|
-
|
|
564
|
-
|
|
613
|
+
Before the release procedure ran, live workflow, registry installation,
|
|
614
|
+
cryptographic provenance and immutable release acceptance were unverified.
|
|
615
|
+
The subsequent scoped-only result is recorded below.
|
|
616
|
+
|
|
617
|
+
### Verified first-release outcome
|
|
618
|
+
|
|
619
|
+
The maintainer narrowed RELEASE-01 to scoped-only publication. On 5 November
|
|
620
|
+
2026, `@n8r/surface@0.1.0-experimental.2` passed exact registry byte/integrity,
|
|
621
|
+
installed library/assets/CLI, isolated startup and signed-provenance checks.
|
|
622
|
+
The GitHub prerelease was finalized and read back as immutable. See the
|
|
623
|
+
[release evidence](npm-release.md#verified-scoped-release). The unscoped
|
|
624
|
+
submission failed with E403 and was removed from the release scope; no unscoped
|
|
625
|
+
registry success or name protection is claimed. Future workflow adaptation
|
|
626
|
+
must remove dual-package assumptions before another scoped release. Unscoped
|
|
627
|
+
publication and name reservation are no longer planned.
|
|
628
|
+
|
|
629
|
+
## SKILL-PRESENTATION-01: read a useful checked report
|
|
630
|
+
|
|
631
|
+
**Implemented candidate 0.1.11.** Given a saved checked Capability/Bound bundle,
|
|
632
|
+
ask the installed Researcher to save a readable presentation copy in a new folder,
|
|
633
|
+
using the same bundle and original predecessor. Expect the current conclusion,
|
|
634
|
+
what changed, bounded significance, cited evidence, decisions and unknowns in
|
|
635
|
+
both HTML and brief. Exact-parent comparison is unavailable when parent research
|
|
636
|
+
is not retained. All checked data and original bytes remain unchanged; the new
|
|
637
|
+
manifest covers the new presentation. No new research or CLI behavior is added.
|
|
638
|
+
See the [human task and readback](skill-surface-artifacts.md#short-human-check).
|
|
639
|
+
|
|
640
|
+
## SKILL-SURFACE-02: save and continue an investigation
|
|
641
|
+
|
|
642
|
+
**Candidate 0.1.13 implemented; installed assessment tracked separately.**
|
|
643
|
+
No new product CLI command. Given current-chat supplied sources and an authorized
|
|
644
|
+
Surface destination, ask: “Explain the AI paths, save the Surface and maintain an
|
|
645
|
+
investigation journal as we go.” Expect an initial plan, possible/selected paths,
|
|
646
|
+
inspectable evidence, useful next options and numbered saved revisions.
|
|
647
|
+
|
|
648
|
+
The state change is a self-contained CycloneDX BOM plus derived brief, HTML map,
|
|
649
|
+
Markdown journal and manifest. Given only the latest BOM in a fresh chat, ask to
|
|
650
|
+
continue: evidence, decisions, uncertainties and investigation state must recover.
|
|
651
|
+
A user-directed diversion remains recorded, with deferred work and its reason.
|
|
652
|
+
Earlier bytes remain unchanged. View edits require explicit reconciliation.
|
|
653
|
+
See the [human tasks and evidence](skill-bom-verification.md) and
|
|
654
|
+
[working manual](skill-manual.md). Automated profile checks do not establish
|
|
655
|
+
research accuracy or human acceptance.
|
|
656
|
+
|
|
657
|
+
## SKILL-OVERVIEW-01: understand a supplied application
|
|
658
|
+
|
|
659
|
+
Given the identified 0.1.14 candidate, readable source and a new output folder,
|
|
660
|
+
when the user asks for an initial architecture/security overview and saves it,
|
|
661
|
+
the brief explains application meaning, major areas, evidence-backed implications,
|
|
662
|
+
coverage gaps and next decisions. The HTML shows connected nodes once, before
|
|
663
|
+
relationship/evidence detail. Intended choices remain separate from configuration
|
|
664
|
+
and dated observations. Existing revisions stay unchanged.
|
|
665
|
+
|
|
666
|
+
Specified in [the first-use verification](first-use-overview.md); renderer and
|
|
667
|
+
research guidance implemented. Installed rehearsal and human acceptance status
|
|
668
|
+
are recorded there. Automated structure checks alone do not verify this outcome.
|
|
669
|
+
|
|
670
|
+
### SKILL-OVERVIEW-02: continue a source-only investigation
|
|
671
|
+
|
|
672
|
+
Given a saved source investigation with historical runtime blockers and completed
|
|
673
|
+
work, say: “It is just a source checkout. Record that in the Surface, keep
|
|
674
|
+
deployment settings unverified, and show the most useful source-level investigation
|
|
675
|
+
next. Save a successor.” Expect a short current scope, reconciled findings/gaps,
|
|
676
|
+
one recommended source action, and unchanged predecessor bytes. Obsolete blockers
|
|
677
|
+
and actions remain history, not current requirements. No runtime inventory or
|
|
678
|
+
service startup is needed. The brief discloses omitted list items and links full
|
|
679
|
+
detail; it retains limitations beside displayed findings.
|
|
680
|
+
|
|
681
|
+
For separately requested inspection of an identified runtime target, emit only
|
|
682
|
+
that target's relevant safe fields. A broad response must be filtered inside the
|
|
683
|
+
collecting process before output; saved redaction alone is insufficient.
|
|
684
|
+
See [rehearsal evidence and human checks](first-use-overview.md#follow-up-rehearsal).
|
|
685
|
+
|
|
686
|
+
## SKILL-INSTANCE-01: understand and revisit a running composition
|
|
687
|
+
|
|
688
|
+
Candidate 0.1.15 adds research guidance; no product CLI command or access adapter.
|
|
689
|
+
Given an identified target, permitted host access to sources/instance evidence and
|
|
690
|
+
a private destination, ask: “Understand this running system, show what is
|
|
691
|
+
configured, observed and untested, and save the Surface.” Expect source identity,
|
|
692
|
+
scoped observations, a useful composition beyond a single repository, explicit
|
|
693
|
+
access gaps and saved BOM/brief/map/journal readback. No probes or service changes.
|
|
694
|
+
|
|
695
|
+
Continue from that BOM with current permitted evidence. Expect unchanged stable
|
|
696
|
+
identities and predecessor bytes, with actual target changes distinguished from
|
|
697
|
+
newly learned facts, unchanged checked facts, lost coverage and conflicts. A host
|
|
698
|
+
without required access or saving tools must report the blocked portion without
|
|
699
|
+
fabricating results. Source-only requests need no runtime inventory.
|
|
700
|
+
See the [human tasks and bounded verification](instance-first-research.md).
|
|
701
|
+
|
|
702
|
+
## SKILL-ACCESS-01: begin without prepared connections
|
|
703
|
+
|
|
704
|
+
Given a fresh session and a description/document, with laptop source and VPS
|
|
705
|
+
access only mentioned, ask Researcher to understand the system and help provide
|
|
706
|
+
sources. Expect a provisional map, one useful next source and host-appropriate
|
|
707
|
+
access guidance. A project upload is not a live folder; SSH details are not access.
|
|
708
|
+
Supply another source and expect progress rather than repeated intake. Saving
|
|
709
|
+
requires observed helper/runtime/delivery capabilities; otherwise retain copyable
|
|
710
|
+
findings and name the blocked step. No credentials in chat or target mutations.
|
|
711
|
+
See [exact interactions, readback and verification status](first-use-access.md).
|
|
@@ -12,6 +12,16 @@ make this profile concrete. Other languages can implement the same data contract
|
|
|
12
12
|
The library reads supplied strings and returns values/strings; it does not read
|
|
13
13
|
files, fetch references, call inference or write a corpus.
|
|
14
14
|
|
|
15
|
+
The separate JSON-only [Capability and Bound contract 0.2](surface-description-contract.md)
|
|
16
|
+
adds possible relationships and configured instance bindings without reinterpreting
|
|
17
|
+
these selected `0.1` definitions. The APIs accept both versions; their meanings
|
|
18
|
+
and fields remain distinct. Installed-skill production is separate work.
|
|
19
|
+
|
|
20
|
+
The [shared relationship contract 0.3](surface-relationships.md) adds the same
|
|
21
|
+
component-node and relationship-edge structure to both descriptions, including
|
|
22
|
+
evidenced purpose, integration mechanism and safe configuration references.
|
|
23
|
+
Existing `0.1` and `0.2` representations remain readable.
|
|
24
|
+
|
|
15
25
|
## Common envelope and identity
|
|
16
26
|
|
|
17
27
|
Every artefact has these required fields. Objects are closed except the explicit
|
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# n8r-surface critical path
|
|
2
2
|
|
|
3
|
+
The [security assurance strategy agreed on 12 November 2026](security-assurance-strategy.md)
|
|
4
|
+
sets the destination: a consultant-led framework connecting policy and levels to
|
|
5
|
+
controls, implementation and observable tests. Its
|
|
6
|
+
[milestone map](security-assurance-strategy.md#existing-work-and-proposed-next-milestones)
|
|
7
|
+
places the existing skill, pending acceptance and proposed first framework slice
|
|
8
|
+
in that context. The delivery history below does not define a complete assurance
|
|
9
|
+
framework or establish a posture score.
|
|
10
|
+
|
|
11
|
+
The [product direction captured on 11 November 2026](product-direction.md)
|
|
12
|
+
proposes a first value demonstration: installed-skill discovery across sources,
|
|
13
|
+
a bounded security finding on an integrated test system, and a verified defence.
|
|
14
|
+
It accepts unintegrated sources for discovery and requires runtime evidence for
|
|
15
|
+
runtime claims. This is a proposed acceptance target, not implementation or
|
|
16
|
+
rehearsal evidence; the delivery history and existing obligations below remain.
|
|
17
|
+
|
|
18
|
+
The next installed-skill delivery agreed on 8 November 2026 is
|
|
19
|
+
[Capability and Bound Surfaces from workspace artifacts](surface-descriptions.md),
|
|
20
|
+
tracked by #50 → #51 → #52 under #17. Observed Surface is deferred. The earlier
|
|
21
|
+
CLI sequence and evidence below remain historical context, not this increment's
|
|
22
|
+
completion criteria.
|
|
23
|
+
|
|
3
24
|
The user updated delivery order on 5 November 2026: standalone Codex research,
|
|
4
25
|
Markdown save/reopen, then partial Surface generation and a complete fresh-session
|
|
5
26
|
revision proof. npm distribution ([#27](https://github.com/injectionator/n8r-surface/issues/27))
|