harness-everything 0.12.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.github/workflows/ci.yml +2 -0
  4. package/CHANGELOG.md +30 -0
  5. package/ci/mechanism-2aj-waza-reference-adapter.test.js +223 -0
  6. package/ci/mechanism-2ak-contract-probe-adapter.test.js +207 -0
  7. package/ci/openai-plugin-package.test.js +10 -0
  8. package/contract-integrity/ADAPTERS.md +37 -0
  9. package/contract-integrity/fixtures/node-probe-project/package.json +8 -0
  10. package/contract-integrity/fixtures/node-probe-project/probe-plan.json +27 -0
  11. package/contract-integrity/fixtures/node-probe-project/src/limit.js +8 -0
  12. package/contract-integrity/fixtures/node-probe-project/test/contract.test.js +14 -0
  13. package/contract-integrity/scripts/node-probe-adapter.js +390 -0
  14. package/docs/repository-contract.md +1 -1
  15. package/evals/multi-agent-workspace/tasks/positive-trigger.yaml +1 -1
  16. package/fable-mode/SKILL.md +1 -1
  17. package/fable-mode/execution-guardrails/SKILL.md +1 -1
  18. package/fable-mode/fable-haiku/SKILL.md +1 -1
  19. package/fable-mode/fable-opus/SKILL.md +1 -1
  20. package/fable-mode/fable-sonnet/SKILL.md +1 -1
  21. package/fable-mode/references/model-matrix.md +2 -0
  22. package/harness-everything/SKILL.md +1 -1
  23. package/harness-everything/references/triage-and-tiers.md +2 -0
  24. package/multi-agent-workspace/SKILL.md +2 -2
  25. package/multi-agent-workspace/references/orchestration.md +2 -0
  26. package/opencode-plugin/plugin.json +1 -1
  27. package/package.json +4 -2
  28. package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
  29. package/plugins/harness-everything/contract-integrity/ADAPTERS.md +37 -0
  30. package/plugins/harness-everything/contract-integrity/scripts/node-probe-adapter.js +390 -0
  31. package/plugins/harness-everything/plugin.json +1 -1
  32. package/plugins/harness-everything/skills/fable-mode/SKILL.md +1 -1
  33. package/plugins/harness-everything/skills/fable-mode/execution-guardrails/SKILL.md +1 -1
  34. package/plugins/harness-everything/skills/fable-mode/fable-haiku/SKILL.md +1 -1
  35. package/plugins/harness-everything/skills/fable-mode/fable-opus/SKILL.md +1 -1
  36. package/plugins/harness-everything/skills/fable-mode/fable-sonnet/SKILL.md +1 -1
  37. package/plugins/harness-everything/skills/fable-mode/references/model-matrix.md +2 -0
  38. package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
  39. package/plugins/harness-everything/skills/harness-everything/references/triage-and-tiers.md +2 -0
  40. package/plugins/harness-everything/skills/multi-agent-workspace/SKILL.md +2 -2
  41. package/plugins/harness-everything/skills/multi-agent-workspace/references/orchestration.md +2 -0
  42. package/plugins/harness-everything/skills/skill-creator/SKILL.md +1 -1
  43. package/plugins/harness-everything/skills/skill-creator/references/quality-checklist.md +2 -0
  44. package/plugins/harness-everything/skills/tdd/SKILL.md +1 -1
  45. package/plugins/harness-everything/skills/tdd/references/core-discipline.md +1 -1
  46. package/plugins/harness-everything/skills/tdd/references/quality-model.md +2 -2
  47. package/scripts/sync-openai-plugin.js +4 -1
  48. package/scripts/waza-harness-check.js +361 -0
  49. package/skill-creator/SKILL.md +1 -1
  50. package/skill-creator/references/quality-checklist.md +2 -0
  51. package/tdd/SKILL.md +1 -1
  52. package/tdd/references/core-discipline.md +1 -1
  53. package/tdd/references/quality-model.md +2 -2
@@ -0,0 +1,390 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const crypto = require('crypto');
5
+ const fs = require('fs');
6
+ const os = require('os');
7
+ const path = require('path');
8
+ const { spawnSync } = require('child_process');
9
+
10
+ const VERSION = '1.0.0';
11
+ const ADAPTER = 'node-npm-v1';
12
+ const STRATEGIES = new Set(['env', 'replace']);
13
+ const DEFAULT_TIMEOUT_MS = 30000;
14
+ const MAX_TIMEOUT_MS = 120000;
15
+
16
+ function isObject(value) {
17
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
18
+ }
19
+
20
+ function safeRelative(value) {
21
+ if (typeof value !== 'string' || !value.trim() || path.isAbsolute(value)) return false;
22
+ const normalized = value.replace(/\\/g, '/');
23
+ return normalized !== '..' && !normalized.startsWith('../') && !normalized.includes('/../') && !normalized.includes('\0');
24
+ }
25
+
26
+ function sha256(value) {
27
+ return crypto.createHash('sha256').update(String(value || ''), 'utf8').digest('hex');
28
+ }
29
+
30
+ function validatePlan(plan) {
31
+ const errors = [];
32
+ if (!isObject(plan)) return ['probe plan must be an object'];
33
+ if (plan.schemaVersion !== VERSION) errors.push(`schemaVersion must be ${VERSION}`);
34
+ if (plan.adapter !== ADAPTER) errors.push(`adapter must be ${ADAPTER}`);
35
+ if (typeof plan.script !== 'string' || !/^[A-Za-z0-9:_-]+$/.test(plan.script)) errors.push('script must be an npm script name');
36
+ if (!Array.isArray(plan.probes) || plan.probes.length === 0) errors.push('probes must be a non-empty array');
37
+ const ids = new Set();
38
+ for (const [index, probe] of (Array.isArray(plan.probes) ? plan.probes : []).entries()) {
39
+ const p = `probes[${index}]`;
40
+ if (!isObject(probe)) { errors.push(`${p} must be an object`); continue; }
41
+ for (const field of ['probeId', 'requirementId', 'sourceSection', 'strategy', 'expectedFailureContains']) {
42
+ if (typeof probe[field] !== 'string' || !probe[field].trim()) errors.push(`${p}.${field} is required`);
43
+ }
44
+ if (probe.probeId && ids.has(probe.probeId)) errors.push(`${p}.probeId is duplicated: ${probe.probeId}`);
45
+ if (probe.probeId) ids.add(probe.probeId);
46
+ if (!STRATEGIES.has(probe.strategy)) errors.push(`${p}.strategy must be env or replace`);
47
+ if (probe.strategy === 'env') {
48
+ if (!isObject(probe.env) || Object.keys(probe.env).length === 0) errors.push(`${p}.env is required for env strategy`);
49
+ for (const [key, value] of Object.entries(isObject(probe.env) ? probe.env : {})) {
50
+ if (!/^[A-Z][A-Z0-9_]{0,79}$/.test(key)) errors.push(`${p}.env contains invalid key: ${key}`);
51
+ if (typeof value !== 'string' || value.length > 200) errors.push(`${p}.env.${key} must be a string <= 200 chars`);
52
+ }
53
+ }
54
+ if (probe.strategy === 'replace') {
55
+ if (!safeRelative(probe.target)) errors.push(`${p}.target must be a repo-relative path`);
56
+ if (typeof probe.find !== 'string' || probe.find.length === 0 || probe.find.length > 4000) errors.push(`${p}.find must be 1..4000 chars`);
57
+ if (typeof probe.replace !== 'string' || probe.replace.length > 4000) errors.push(`${p}.replace must be <= 4000 chars`);
58
+ }
59
+ }
60
+ if (plan.timeoutMs !== undefined && (!Number.isInteger(plan.timeoutMs) || plan.timeoutMs < 100 || plan.timeoutMs > MAX_TIMEOUT_MS)) {
61
+ errors.push(`timeoutMs must be an integer between 100 and ${MAX_TIMEOUT_MS}`);
62
+ }
63
+ return errors;
64
+ }
65
+
66
+ function discoverProject(workspace, plan) {
67
+ const packageFile = path.join(workspace, 'package.json');
68
+ if (!fs.existsSync(packageFile)) return { ok: false, reason: 'package-json-missing' };
69
+ let pkg;
70
+ try { pkg = JSON.parse(fs.readFileSync(packageFile, 'utf8')); }
71
+ catch (_) { return { ok: false, reason: 'package-json-invalid' }; }
72
+ if (!isObject(pkg.scripts) || typeof pkg.scripts[plan.script] !== 'string') {
73
+ return { ok: false, reason: 'npm-script-missing' };
74
+ }
75
+ const dependencyCount =
76
+ Object.keys(isObject(pkg.dependencies) ? pkg.dependencies : {}).length +
77
+ Object.keys(isObject(pkg.devDependencies) ? pkg.devDependencies : {}).length +
78
+ Object.keys(isObject(pkg.optionalDependencies) ? pkg.optionalDependencies : {}).length;
79
+ if (dependencyCount > 0) {
80
+ return {
81
+ ok: false,
82
+ reason: 'external-dependencies-not-supported-v1',
83
+ detail: 'node-npm-v1 intentionally supports zero-external-dependency projects only; dependency installation is never inferred or networked',
84
+ };
85
+ }
86
+ return {
87
+ ok: true,
88
+ packageName: typeof pkg.name === 'string' ? pkg.name : null,
89
+ script: plan.script,
90
+ command: `npm run ${plan.script}`,
91
+ dependencyMode: 'zero-external-dependencies',
92
+ };
93
+ }
94
+
95
+ function copyWorkspace(source) {
96
+ const sourceRoot = path.resolve(source);
97
+ if (fs.lstatSync(sourceRoot).isSymbolicLink()) {
98
+ throw new Error('source workspace contains a symbolic link/reparse-point root');
99
+ }
100
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-contract-probe-'));
101
+ const destination = path.join(root, 'workspace');
102
+ try {
103
+ fs.cpSync(sourceRoot, destination, {
104
+ recursive: true,
105
+ filter: src => {
106
+ if (fs.lstatSync(src).isSymbolicLink()) {
107
+ throw new Error('source workspace contains a symbolic link/reparse-point component');
108
+ }
109
+ const relative = path.relative(sourceRoot, src).replace(/\\/g, '/');
110
+ if (!relative) return true;
111
+ const head = relative.split('/')[0];
112
+ return !['.git', 'node_modules', '.harness'].includes(head);
113
+ },
114
+ });
115
+ } catch (error) {
116
+ fs.rmSync(root, { recursive: true, force: true });
117
+ throw error;
118
+ }
119
+ return { root, workspace: destination };
120
+ }
121
+
122
+ function runScript(workspace, plan, extraEnv = {}) {
123
+ const timeout = plan.timeoutMs || DEFAULT_TIMEOUT_MS;
124
+ // On Windows npm is normally a .cmd shim, which child_process cannot
125
+ // execute directly without a command shell. The script name is restricted
126
+ // to a safe identifier by validatePlan(), and no user-provided shell text is
127
+ // interpolated into the command.
128
+ const result = spawnSync('npm', ['run', plan.script], {
129
+ cwd: workspace,
130
+ encoding: 'utf8',
131
+ timeout,
132
+ windowsHide: true,
133
+ shell: process.platform === 'win32',
134
+ env: { ...process.env, CI: '1', ...extraEnv },
135
+ });
136
+ const stdout = String(result.stdout || '');
137
+ const stderr = String(result.stderr || '');
138
+ const combined = stdout + '\n' + stderr;
139
+ if (result.error) {
140
+ const timedOut = result.error.code === 'ETIMEDOUT';
141
+ return {
142
+ kind: timedOut ? 'timeout' : 'spawn-error',
143
+ exitCode: null,
144
+ signal: result.signal || null,
145
+ outputHash: sha256(combined + String(result.error.message || '')),
146
+ combined,
147
+ };
148
+ }
149
+ return {
150
+ kind: 'exit',
151
+ exitCode: Number.isInteger(result.status) ? result.status : null,
152
+ signal: result.signal || null,
153
+ outputHash: sha256(combined),
154
+ combined,
155
+ };
156
+ }
157
+
158
+ function isolatedRegularFile(workspace, relativeTarget) {
159
+ const root = path.resolve(workspace);
160
+ const target = path.resolve(root, relativeTarget);
161
+ const lexical = path.relative(root, target);
162
+ if (lexical === '..' || lexical.startsWith('..' + path.sep) || path.isAbsolute(lexical)) {
163
+ throw new Error('replace target escaped isolated workspace');
164
+ }
165
+
166
+ let current = root;
167
+ for (const segment of lexical.split(path.sep).filter(Boolean)) {
168
+ current = path.join(current, segment);
169
+ if (!fs.existsSync(current)) throw new Error('replace target does not exist');
170
+ const stat = fs.lstatSync(current);
171
+ if (stat.isSymbolicLink()) {
172
+ throw new Error('replace target contains symbolic link/reparse-point component');
173
+ }
174
+ }
175
+
176
+ const realRoot = fs.realpathSync(root);
177
+ const realTarget = fs.realpathSync(target);
178
+ const resolved = path.relative(realRoot, realTarget);
179
+ if (resolved === '..' || resolved.startsWith('..' + path.sep) || path.isAbsolute(resolved)) {
180
+ throw new Error('replace target resolved outside isolated workspace');
181
+ }
182
+ if (!fs.lstatSync(realTarget).isFile()) throw new Error('replace target does not exist');
183
+ return realTarget;
184
+ }
185
+
186
+ function applyProbe(workspace, probe) {
187
+ if (probe.strategy === 'env') return { env: { ...probe.env }, mutation: 'environment-only' };
188
+
189
+ const target = isolatedRegularFile(workspace, probe.target);
190
+ const source = fs.readFileSync(target, 'utf8');
191
+ const first = source.indexOf(probe.find);
192
+ const last = source.lastIndexOf(probe.find);
193
+ if (first < 0) throw new Error('replace find text was not present');
194
+ if (first !== last) throw new Error('replace find text must match exactly once');
195
+ fs.writeFileSync(target, source.slice(0, first) + probe.replace + source.slice(first + probe.find.length), 'utf8');
196
+ return { env: {}, mutation: `replace:${probe.target}` };
197
+ }
198
+
199
+ function evidenceRef(probe, run) {
200
+ return `probe:${probe.probeId}:output-sha256:${run.outputHash}`;
201
+ }
202
+
203
+ function notEvaluatedProbe(probe, reason, failureClass = 'infrastructure', workspaceIsolation = 'isolated') {
204
+ return {
205
+ probeId: probe.probeId,
206
+ requirementId: probe.requirementId,
207
+ status: 'NOT_EVALUATED',
208
+ sourceSection: probe.sourceSection,
209
+ evidenceRef: null,
210
+ workspaceIsolation,
211
+ failureClass,
212
+ adapterReason: reason,
213
+ };
214
+ }
215
+
216
+ function executePlan(workspace, plan) {
217
+ const errors = validatePlan(plan);
218
+ if (errors.length) return { schemaVersion: VERSION, adapter: ADAPTER, result: 'INVALID_PLAN', errors, probes: [] };
219
+
220
+ const discovered = discoverProject(workspace, plan);
221
+ if (!discovered.ok) {
222
+ return {
223
+ schemaVersion: VERSION,
224
+ adapter: ADAPTER,
225
+ result: 'NOT_EVALUATED',
226
+ discovery: discovered,
227
+ baseline: null,
228
+ probes: plan.probes.map(probe => notEvaluatedProbe(probe, discovered.reason)),
229
+ errors: [],
230
+ };
231
+ }
232
+
233
+ let baselineIsolation;
234
+ try {
235
+ baselineIsolation = copyWorkspace(workspace);
236
+ const baselineRun = runScript(baselineIsolation.workspace, plan);
237
+ const baseline = {
238
+ status: baselineRun.kind === 'exit' && baselineRun.exitCode === 0 ? 'PASS' : 'FAIL',
239
+ exitCode: baselineRun.exitCode,
240
+ failureClass: baselineRun.kind === 'exit' ? (baselineRun.exitCode === 0 ? 'none' : 'baseline-test') : 'infrastructure',
241
+ evidenceRef: `baseline:output-sha256:${baselineRun.outputHash}`,
242
+ workspaceIsolation: 'isolated',
243
+ };
244
+ if (baseline.status !== 'PASS') {
245
+ return {
246
+ schemaVersion: VERSION,
247
+ adapter: ADAPTER,
248
+ result: 'NOT_EVALUATED',
249
+ discovery: discovered,
250
+ baseline,
251
+ probes: plan.probes.map(probe => notEvaluatedProbe(probe, 'baseline-not-green')),
252
+ errors: [],
253
+ };
254
+ }
255
+ } catch (error) {
256
+ const reason = String(error.message || error);
257
+ return {
258
+ schemaVersion: VERSION,
259
+ adapter: ADAPTER,
260
+ result: 'NOT_EVALUATED',
261
+ discovery: discovered,
262
+ baseline: {
263
+ status: 'NOT_EVALUATED',
264
+ evidenceRef: null,
265
+ workspaceIsolation: 'not-isolated',
266
+ failureClass: 'infrastructure',
267
+ adapterReason: reason,
268
+ },
269
+ probes: plan.probes.map(probe =>
270
+ notEvaluatedProbe(probe, reason, 'infrastructure', 'not-isolated')),
271
+ errors: [],
272
+ };
273
+ } finally {
274
+ if (baselineIsolation?.root) fs.rmSync(baselineIsolation.root, { recursive: true, force: true });
275
+ }
276
+
277
+ const results = [];
278
+ for (const probe of plan.probes) {
279
+ let isolated;
280
+ try {
281
+ isolated = copyWorkspace(workspace);
282
+ const applied = applyProbe(isolated.workspace, probe);
283
+ const run = runScript(isolated.workspace, plan, applied.env);
284
+ if (run.kind !== 'exit') {
285
+ results.push(notEvaluatedProbe(probe, run.kind));
286
+ continue;
287
+ }
288
+ if (run.exitCode === 0) {
289
+ results.push({
290
+ probeId: probe.probeId,
291
+ requirementId: probe.requirementId,
292
+ status: 'SURVIVED',
293
+ sourceSection: probe.sourceSection,
294
+ evidenceRef: evidenceRef(probe, run),
295
+ workspaceIsolation: 'isolated',
296
+ failureClass: 'none',
297
+ adapterReason: 'project-test-suite-remained-green',
298
+ });
299
+ continue;
300
+ }
301
+ if (run.combined.includes(probe.expectedFailureContains)) {
302
+ results.push({
303
+ probeId: probe.probeId,
304
+ requirementId: probe.requirementId,
305
+ status: 'KILLED',
306
+ sourceSection: probe.sourceSection,
307
+ evidenceRef: evidenceRef(probe, run),
308
+ workspaceIsolation: 'isolated',
309
+ failureClass: 'contract',
310
+ adapterReason: 'expected-observable-contract-failure',
311
+ });
312
+ } else {
313
+ results.push({
314
+ probeId: probe.probeId,
315
+ requirementId: probe.requirementId,
316
+ status: 'INVALID',
317
+ sourceSection: probe.sourceSection,
318
+ evidenceRef: evidenceRef(probe, run),
319
+ workspaceIsolation: 'isolated',
320
+ failureClass: 'unknown',
321
+ adapterReason: 'test-failed-without-expected-contract-observable',
322
+ });
323
+ }
324
+ } catch (error) {
325
+ results.push(notEvaluatedProbe(
326
+ probe,
327
+ String(error.message || error),
328
+ 'infrastructure',
329
+ isolated ? 'isolated' : 'not-isolated',
330
+ ));
331
+ } finally {
332
+ if (isolated?.root) fs.rmSync(isolated.root, { recursive: true, force: true });
333
+ }
334
+ }
335
+
336
+ return {
337
+ schemaVersion: VERSION,
338
+ adapter: ADAPTER,
339
+ result: results.every(probe => probe.status === 'KILLED') ? 'PROTECTED' :
340
+ results.some(probe => probe.status === 'SURVIVED') ? 'SURVIVED' : 'NOT_EVALUATED',
341
+ discovery: discovered,
342
+ baseline: { status: 'PASS', workspaceIsolation: 'isolated' },
343
+ probes: results,
344
+ errors: [],
345
+ };
346
+ }
347
+
348
+ function parseArgs(argv) {
349
+ const args = { workspace: null, plan: null, output: null };
350
+ for (let i = 0; i < argv.length; i++) {
351
+ if (argv[i] === '--workspace') args.workspace = argv[++i];
352
+ else if (argv[i] === '--plan') args.plan = argv[++i];
353
+ else if (argv[i] === '--output') args.output = argv[++i];
354
+ else throw new Error('unknown argument: ' + argv[i]);
355
+ }
356
+ if (!args.workspace || !args.plan) throw new Error('Usage: node node-probe-adapter.js --workspace <project> --plan <probe-plan.json> [--output report.json]');
357
+ return args;
358
+ }
359
+
360
+ function main() {
361
+ try {
362
+ const args = parseArgs(process.argv.slice(2));
363
+ const workspace = path.resolve(args.workspace);
364
+ const plan = JSON.parse(fs.readFileSync(path.resolve(args.plan), 'utf8'));
365
+ const report = executePlan(workspace, plan);
366
+ const json = JSON.stringify(report, null, 2) + '\n';
367
+ if (args.output) {
368
+ fs.mkdirSync(path.dirname(path.resolve(args.output)), { recursive: true });
369
+ fs.writeFileSync(path.resolve(args.output), json, 'utf8');
370
+ }
371
+ process.stdout.write(json);
372
+ process.exitCode = report.result === 'PROTECTED' ? 0 : report.result === 'SURVIVED' ? 1 : 2;
373
+ } catch (error) {
374
+ console.error('[Contract Probe Adapter] ' + error.message);
375
+ process.exitCode = 2;
376
+ }
377
+ }
378
+
379
+ if (require.main === module) main();
380
+
381
+ module.exports = {
382
+ ADAPTER,
383
+ VERSION,
384
+ applyProbe,
385
+ discoverProject,
386
+ executePlan,
387
+ runScript,
388
+ safeRelative,
389
+ validatePlan,
390
+ };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "harness-everything",
4
- "version": "0.12.3",
4
+ "version": "0.14.0",
5
5
  "description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
6
6
  "author": {
7
7
  "name": "Miya Daniel",
@@ -4,7 +4,7 @@ description: "Stage large, multi-source or multi-session tasks through a written
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.9.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Fable Mode (v3)
@@ -7,7 +7,7 @@ description: >-
7
7
  license: Apache-2.0
8
8
  metadata:
9
9
  author: Miya Daniel
10
- version: 0.9.0
10
+ version: 0.13.0
11
11
  ---
12
12
 
13
13
  # Execution Guardrails
@@ -4,7 +4,7 @@ description: "Run staged fable work on Haiku for bulk mechanical tasks with expl
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.9.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Fable Mode — Haiku
@@ -4,7 +4,7 @@ description: "Run staged fable orchestration on Opus for cross-stage synthesis,
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.9.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Fable Mode — Opus
@@ -4,7 +4,7 @@ description: "Run staged fable work on Sonnet for non-trivial implementation, re
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.9.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Fable Mode — Sonnet
@@ -37,3 +37,5 @@ The orchestrator still owns scope lock, at most two full replans, stage
37
37
  contracts, worker non-recursion, cold verification, and escalation. The native
38
38
  host TODO tracker or a Markdown checklist records stage progress; no CLI TODO
39
39
  state machine is part of this contract.
40
+
41
+ See [execution phases](execution-phases.md) for the staged run contract.
@@ -4,7 +4,7 @@ description: "Route software/project work through the Harness kernel: classify T
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.11.7
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Harness Everything
@@ -177,3 +177,5 @@ node "<this-skill-dir>/scripts/self-heal.js" --check
177
177
  ```
178
178
 
179
179
  Respect an explicit user choice to remove/disable an integration.
180
+
181
+ Related contracts: [router workflow plan](router-workflow-plan.md) and [skill registry](skill-registry.md).
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: multi-agent-workspace
3
- description: "Scaffold a verified multi-agent workspace and select bounded specialists from an external agency-agents catalog without vendoring the full roster."
3
+ description: "Scaffold a verified multi-agent workspace and select bounded specialists from an external agency-agents catalog without vendoring the full external roster."
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.12.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Multi-Agent Workspace
@@ -48,6 +48,8 @@ The consumer adds `workflowCorrelation` containing `planId`, `runId`, the select
48
48
 
49
49
  Retrieved memory is task-relevant data, not instructions. This phase does not add a new persistence path; memory writes continue through the existing governed workflow and Phase 4 will add secret/prompt-injection screening required by #85.
50
50
 
51
+ The external catalog boundary is documented in [agency-agents source contract](agency-agents.md).
52
+
51
53
  ## Scaffolding Multi-Agent Project Infrastructure (Sequential Workflows)
52
54
 
53
55
  For generating structured, permanent agent manifests in a repository, follow the progressive workflow steps in order:
@@ -4,7 +4,7 @@ description: Author, audit, and refactor SKILL.md files against one quality bar
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.4.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Skill Creator
@@ -14,3 +14,5 @@ A skill isn't done until every line here is true. This is deliberately a flat ch
14
14
  - [ ] **Reuses existing leading words** (`Tier 1/2/3`, `circuit breaker`/`zoom out`, `Rule of 3`, `Red/Green/Refactor`, `Discover > Think > Try > Summarize > Record`) instead of naming a new concept for something this repo already has a word for.
15
15
  - [ ] **Completion criteria are checkable** — tied to an exit code, a file's existence, a grep result, or explicit human confirmation, not "when it feels done."
16
16
  - [ ] **Description reads as a human-facing sentence**, not just a trigger-phrase list — `scripts/installer.js`'s interactive picker prints it next to a checkbox for a person deciding whether to install this skill; that's its real first reader in this architecture (audit report §2.1).
17
+
18
+ Related guides: [authoring workflow](authoring-workflow.md), [testing workflow](testing-workflow.md), [quality principles](quality-principles.md), and [dynamic generation contract](dynamic-generation-contract.md).
@@ -4,7 +4,7 @@ description: "Drive Standard Tasks (Tier 2) with Test-Driven Development (RED/GR
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: Miya Daniel
7
- version: 0.4.0
7
+ version: 0.13.0
8
8
  ---
9
9
 
10
10
  # Test-Driven Development
@@ -22,7 +22,7 @@ flowchart TD
22
22
 
23
23
  When this skill is loaded, you MUST suppress the urge to write implementation code directly, and strictly follow these three phases.
24
24
 
25
- For public behavior, RED/GREEN evidence is necessary but not sufficient. Apply the machine-verifiable matrix, source-conformance policy, determinism comparison, and mandatory gates in `quality-model.md` before completion.
25
+ For public behavior, RED/GREEN evidence is necessary but not sufficient. Apply the machine-verifiable matrix, source-conformance policy, determinism comparison, and mandatory gates in the [quality model](quality-model.md) before completion.
26
26
 
27
27
  **Evidence Assertion (Law of Evidence Assertion - 證據斷言定律)**:
28
28
  During each TDD phase (RED, GREEN, REFACTOR), you MUST present actual terminal test execution logs showing the exact test failure or pass. Claiming progress or success based on "assumed" passes is strictly PROHIBITED; you must assert outcomes with hard, objective evidence.
@@ -6,8 +6,8 @@ This model turns each source requirement into auditable evidence. The authoritat
6
6
 
7
7
  Every requirement declares exactly one `testProfile`: `unit` or `integration`. The selected profile defines which case classes are applicable; the common scoring and source rules do not make every test use identical evidence.
8
8
 
9
- - `unit`: follow `unit-testing.md`. Integration determinism is N/A with a written reason.
10
- - `integration`: follow `integration-testing.md`. Determinism is mandatory and cannot be declared N/A.
9
+ - `unit`: follow [unit testing](unit-testing.md). Integration determinism is N/A with a written reason.
10
+ - `integration`: follow [integration testing](integration-testing.md). Determinism is mandatory and cannot be declared N/A.
11
11
 
12
12
  When one task needs both profiles, split it into separate requirement records so each result remains measurable.
13
13
 
@@ -71,8 +71,11 @@ syncTree(path.join(ROOT, 'scripts', 'lib', 'advisory-text.js'), packageAdvisoryT
71
71
  // Contract-integrity is shared runtime, not a directly-routed 27th skill.
72
72
  // Package it under the plugin root and rewrite its one canonical TDD import
73
73
  // to the packaged skill tree.
74
- const packageContractAudit = path.join(pluginRoot, 'contract-integrity', 'scripts', 'audit.js');
74
+ const packageContractRoot = path.join(pluginRoot, 'contract-integrity');
75
+ const packageContractAudit = path.join(packageContractRoot, 'scripts', 'audit.js');
75
76
  syncTree(path.join(ROOT, 'contract-integrity', 'scripts', 'audit.js'), packageContractAudit);
77
+ syncTree(path.join(ROOT, 'contract-integrity', 'scripts', 'node-probe-adapter.js'), path.join(packageContractRoot, 'scripts', 'node-probe-adapter.js'));
78
+ syncTree(path.join(ROOT, 'contract-integrity', 'ADAPTERS.md'), path.join(packageContractRoot, 'ADAPTERS.md'));
76
79
  const contractAuditSource = fs.readFileSync(packageContractAudit, 'utf8');
77
80
  fs.writeFileSync(
78
81
  packageContractAudit,