ultra-builder-pro-cli 0.26.1 → 3.0.2

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 (75) hide show
  1. package/.ultra-template/contexts/TEMPLATE.md +64 -29
  2. package/.ultra-template/north-star.md +36 -30
  3. package/.ultra-template/project-brief.md +28 -0
  4. package/.ultra-template/specs/product.md +4 -0
  5. package/.ultra-template/specs/research-distillate.md +8 -0
  6. package/.ultra-template/tasks.json +1 -0
  7. package/.ultra-template/test-report.json +18 -1
  8. package/CHANGELOG.md +227 -1
  9. package/README.md +161 -59
  10. package/adapters/_shared/host-profile.cjs +68 -12
  11. package/adapters/_shared/plugin-core.cjs +4 -5
  12. package/adapters/_shared/runtime-assets.cjs +11 -2
  13. package/adapters/zcode.js +375 -0
  14. package/bin/delegate-worker.cjs +20 -0
  15. package/bin/delegate.cjs +94 -25
  16. package/bin/install.js +11 -6
  17. package/docs/ARCHITECTURE.md +109 -25
  18. package/docs/ARTIFACT-AUTHORITY.md +204 -30
  19. package/docs/DECISIONS.md +141 -10
  20. package/docs/PHILOSOPHY.md +61 -8
  21. package/docs/PLUGIN-ISOLATION-CONTRACT.md +6 -0
  22. package/docs/RUNTIME-COMPAT-MATRIX.md +106 -13
  23. package/docs/SKILL-AUTHORING.md +14 -9
  24. package/docs/WORKFLOW-LIFECYCLE.md +211 -53
  25. package/docs/evals/adversarial-review-2026-08-14.md +84 -0
  26. package/docs/evals/zcode-automation-2026-08-14.md +103 -0
  27. package/hooks/README.md +31 -3
  28. package/hooks/_common.py +1699 -33
  29. package/hooks/adapters/zcode.py +227 -0
  30. package/hooks/compact_context.py +176 -17
  31. package/hooks/mid_workflow_recall.py +27 -5
  32. package/hooks/post_edit_guard.py +133 -23
  33. package/hooks/session_context.py +722 -11
  34. package/package.json +6 -3
  35. package/skills/ultra-change/SKILL.md +84 -64
  36. package/skills/ultra-change/references/change-contract.md +130 -32
  37. package/skills/ultra-change/references/execution-grant.md +109 -0
  38. package/skills/ultra-change/references/primary-transfer.md +273 -0
  39. package/skills/ultra-change/scripts/validate_primary_transfer.cjs +1928 -0
  40. package/skills/ultra-delegate/SKILL.md +38 -5
  41. package/skills/ultra-delegate/references/delegation-contract.md +21 -1
  42. package/skills/ultra-deliver/SKILL.md +123 -44
  43. package/skills/ultra-deliver/references/baseline-reconciliation.md +5 -3
  44. package/skills/ultra-dev/SKILL.md +173 -43
  45. package/skills/ultra-grilling/SKILL.md +19 -16
  46. package/skills/ultra-grilling/references/reframing.md +7 -3
  47. package/skills/ultra-init/SKILL.md +99 -87
  48. package/skills/ultra-init/references/brownfield-adoption.md +23 -40
  49. package/skills/ultra-init/scripts/init_project.cjs +968 -21
  50. package/skills/ultra-plan/SKILL.md +83 -46
  51. package/skills/ultra-plan/references/task-evidence-v2.md +300 -0
  52. package/skills/ultra-plan/scripts/validate_task_evidence.cjs +859 -0
  53. package/skills/ultra-research/SKILL.md +119 -68
  54. package/skills/ultra-research/references/{03-competitive-landscape.md → 03-alternatives.md} +6 -6
  55. package/skills/ultra-research/references/04-product-strategy.md +11 -2
  56. package/skills/ultra-research/references/21-features-scope.md +8 -0
  57. package/skills/ultra-research/references/22-success-metrics.md +9 -4
  58. package/skills/ultra-research/references/99-synthesis.md +25 -1
  59. package/skills/ultra-research/references/north-star-v2.md +168 -0
  60. package/skills/ultra-research/references/wayfinding.md +50 -0
  61. package/skills/ultra-research/scripts/validate_north_star.cjs +1174 -0
  62. package/skills/ultra-review/SKILL.md +201 -24
  63. package/skills/ultra-review/references/adversarial-evaluation.md +31 -0
  64. package/skills/ultra-review/references/spec.md +5 -0
  65. package/skills/ultra-review/references/unified-schema.md +146 -11
  66. package/skills/ultra-review/references/worker-packet.md +151 -8
  67. package/skills/ultra-review/scripts/review_wait.py +2653 -63
  68. package/skills/ultra-status/SKILL.md +146 -32
  69. package/skills/ultra-tdd/SKILL.md +14 -4
  70. package/skills/ultra-tdd/references/test-execution.md +6 -1
  71. package/skills/ultra-test/SKILL.md +157 -15
  72. package/skills/ultra-test/scripts/validate_review_transport.cjs +549 -0
  73. package/skills/ultra-test/scripts/worktree_digest.cjs +551 -29
  74. package/skills/ultra-think/SKILL.md +5 -10
  75. package/skills/ultra-think/references/interaction-boundary.md +2 -1
@@ -0,0 +1,375 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+ const { spawnSync } = require('node:child_process');
7
+
8
+ const {
9
+ copyHooks,
10
+ copySkills,
11
+ inspectPlugin,
12
+ markManaged,
13
+ publishManagedTrees,
14
+ removeManaged,
15
+ writePluginProvenance,
16
+ } = require('./_shared/plugin-core.cjs');
17
+ const {
18
+ captureAbsent,
19
+ ensureDir,
20
+ isManaged,
21
+ managedMetadata,
22
+ pruneCreatedEmpty,
23
+ removeTree,
24
+ writeAtomic,
25
+ } = require('./_shared/file-ops.cjs');
26
+ const { zcodeBinary } = require('./_shared/host-profile.cjs');
27
+
28
+ const PLUGIN_NAME = 'ultra-builder-pro';
29
+ const INLINE_PLUGIN_ID = `${PLUGIN_NAME}@inline`;
30
+
31
+ function resolveTarget(ctx = {}) {
32
+ if (ctx.configDir) return path.resolve(ctx.configDir);
33
+ const configured = ctx.zcodeHome || ctx.env?.ZCODE_HOME || process.env.ZCODE_HOME;
34
+ return configured ? path.resolve(configured) : path.join(ctx.homeDir || os.homedir(), '.zcode');
35
+ }
36
+
37
+ function resolveCliRoot(ctx = {}) {
38
+ return path.join(resolveTarget(ctx), 'cli');
39
+ }
40
+
41
+ function resolveConfigFile(ctx = {}) {
42
+ return path.join(resolveCliRoot(ctx), 'config.json');
43
+ }
44
+
45
+ function resolveMarketplaceRoot(ctx = {}) {
46
+ return path.join(resolveCliRoot(ctx), 'plugins', 'marketplaces', PLUGIN_NAME);
47
+ }
48
+
49
+ function resolveMarketplaceFile(ctx = {}) {
50
+ return path.join(resolveMarketplaceRoot(ctx), 'marketplace.json');
51
+ }
52
+
53
+ function resolvePluginRoot(ctx = {}) {
54
+ return path.join(resolveMarketplaceRoot(ctx), 'plugin');
55
+ }
56
+
57
+ function resolveRepoRoot(ctx = {}) {
58
+ return ctx.repoRoot || path.resolve(__dirname, '..');
59
+ }
60
+
61
+ function assertPluginScope(ctx = {}) {
62
+ if (ctx.scope === 'local' && !ctx.configDir) {
63
+ throw new Error('ZCode plugins are user-scoped; use global scope or an isolated configDir');
64
+ }
65
+ }
66
+
67
+ function hookProcess(name) {
68
+ return {
69
+ type: 'process',
70
+ command: 'python3',
71
+ args: [`\${ZCODE_PLUGIN_ROOT}/hooks/adapters/zcode.py`, name],
72
+ timeoutMs: 10000,
73
+ };
74
+ }
75
+
76
+ function buildHooksManifest() {
77
+ return {
78
+ hooks: {
79
+ SessionStart: [
80
+ { matcher: 'startup|resume|clear', hooks: [hookProcess('session_context.py')] },
81
+ { matcher: 'compact', hooks: [hookProcess('compact_context.py')] },
82
+ ],
83
+ PreToolUse: [
84
+ { matcher: 'Write|Edit|ApplyPatch|Grep', hooks: [hookProcess('mid_workflow_recall.py')] },
85
+ { matcher: 'Bash', hooks: [hookProcess('block_dangerous_commands.py')] },
86
+ ],
87
+ PostToolUse: [
88
+ { matcher: 'Write|Edit|ApplyPatch', hooks: [hookProcess('post_edit_guard.py')] },
89
+ ],
90
+ },
91
+ };
92
+ }
93
+
94
+ function loadConfig(file) {
95
+ if (!fs.existsSync(file)) return {};
96
+ const value = JSON.parse(fs.readFileSync(file, 'utf8'));
97
+ if (!value || Array.isArray(value) || typeof value !== 'object') {
98
+ throw new Error(`invalid ZCode config: ${file}`);
99
+ }
100
+ if (value.plugins !== undefined && (
101
+ !value.plugins || Array.isArray(value.plugins) || typeof value.plugins !== 'object'
102
+ )) throw new Error(`invalid ZCode plugins config: ${file}`);
103
+ if (value.plugins?.dirs !== undefined && (
104
+ !Array.isArray(value.plugins.dirs)
105
+ || !value.plugins.dirs.every((entry) => typeof entry === 'string' && entry)
106
+ )) throw new Error(`invalid ZCode plugins.dirs config: ${file}`);
107
+ if (value.plugins?.enabled !== undefined && typeof value.plugins.enabled !== 'boolean') {
108
+ throw new Error(`invalid ZCode plugins.enabled config: ${file}`);
109
+ }
110
+ return value;
111
+ }
112
+
113
+ function configOwnership(file, pluginRoot) {
114
+ const exists = fs.existsSync(file);
115
+ const config = loadConfig(file);
116
+ const plugins = config.plugins;
117
+ return {
118
+ file_created: !exists,
119
+ plugins_created: plugins === undefined,
120
+ enabled_created: plugins === undefined || !Object.hasOwn(plugins, 'enabled'),
121
+ enabled_previous: plugins?.enabled,
122
+ dirs_created: plugins === undefined || !Object.hasOwn(plugins, 'dirs'),
123
+ dir_preexisting: Array.isArray(plugins?.dirs) && plugins.dirs.includes(pluginRoot),
124
+ };
125
+ }
126
+
127
+ function registeredConfig(config, pluginRoot) {
128
+ const plugins = config.plugins ? { ...config.plugins } : {};
129
+ const dirs = Array.isArray(plugins.dirs) ? [...plugins.dirs] : [];
130
+ if (!dirs.includes(pluginRoot)) dirs.push(pluginRoot);
131
+ plugins.enabled = true;
132
+ plugins.dirs = dirs;
133
+ return { ...config, plugins };
134
+ }
135
+
136
+ function unregisteredConfig(config, pluginRoot, ownership) {
137
+ if (!config.plugins || Array.isArray(config.plugins) || typeof config.plugins !== 'object') {
138
+ throw new Error('cannot unregister Ultra from an invalid ZCode plugins config');
139
+ }
140
+ const next = { ...config, plugins: { ...config.plugins } };
141
+ if (!ownership.dir_preexisting && Array.isArray(next.plugins.dirs)) {
142
+ next.plugins.dirs = next.plugins.dirs.filter((entry) => entry !== pluginRoot);
143
+ }
144
+ if (ownership.dirs_created && next.plugins.dirs?.length === 0) delete next.plugins.dirs;
145
+
146
+ if (ownership.enabled_created) {
147
+ if (next.plugins.enabled === true) delete next.plugins.enabled;
148
+ } else if (next.plugins.enabled === true && ownership.enabled_previous !== true) {
149
+ next.plugins.enabled = ownership.enabled_previous;
150
+ }
151
+ if (ownership.plugins_created && Object.keys(next.plugins).length === 0) delete next.plugins;
152
+ return next;
153
+ }
154
+
155
+ function buildStaging(repoRoot, staging, publishedRoot) {
156
+ const plugin = path.join(staging, 'plugin');
157
+ const skills = copySkills({ runtime: 'zcode', repoRoot, skillRoot: path.join(plugin, 'skills') });
158
+ copyHooks({
159
+ runtime: 'zcode', repoRoot, hookRoot: path.join(plugin, 'hooks'),
160
+ hooksManifest: buildHooksManifest(),
161
+ });
162
+ const pkg = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
163
+ writeAtomic(path.join(plugin, '.zcode-plugin', 'plugin.json'), `${JSON.stringify({
164
+ name: PLUGIN_NAME,
165
+ version: pkg.version,
166
+ description: 'File-first Ultra Builder Pro workflows for ZCode.',
167
+ author: { name: pkg.author },
168
+ homepage: pkg.homepage,
169
+ repository: pkg.repository?.url || pkg.repository,
170
+ license: pkg.license,
171
+ skills: './skills',
172
+ hooks: './hooks/hooks.json',
173
+ }, null, 2)}\n`);
174
+ writeAtomic(path.join(staging, 'marketplace.json'), `${JSON.stringify({
175
+ name: PLUGIN_NAME,
176
+ plugins: [{
177
+ name: PLUGIN_NAME,
178
+ version: pkg.version,
179
+ description: 'File-first Ultra Builder Pro workflows for ZCode.',
180
+ source: './plugin',
181
+ }],
182
+ }, null, 2)}\n`);
183
+ markManaged(plugin, { adapter: 'zcode', plugin: PLUGIN_NAME, version: pkg.version });
184
+ writePluginProvenance({
185
+ adapter: 'zcode', repoRoot, stagingRoot: plugin, publishedRoot,
186
+ contracts: {
187
+ plugin_manifest: { root: 'plugin', path: '.zcode-plugin/plugin.json' },
188
+ hooks_manifest: { root: 'plugin', path: 'hooks/hooks.json' },
189
+ hook_adapter: { root: 'plugin', path: 'hooks/adapters/zcode.py' },
190
+ },
191
+ });
192
+ return { skills };
193
+ }
194
+
195
+ function shouldRunHostCli(ctx = {}) {
196
+ if (typeof ctx.runHostCli === 'boolean') return ctx.runHostCli;
197
+ return ctx.scope === 'global' && !ctx.configDir;
198
+ }
199
+
200
+ function zcodeCommand(ctx, args) {
201
+ const selected = ctx.zcodeBin || process.env.ZCODE_BIN || zcodeBinary();
202
+ const command = selected.endsWith('.cjs') || selected.endsWith('.js') ? process.execPath : selected;
203
+ const argv = command === selected ? args : [selected, ...args];
204
+ const result = spawnSync(command, argv, {
205
+ encoding: 'utf8',
206
+ timeout: ctx.hostCliTimeoutMs || 30000,
207
+ env: { ...process.env, HOME: ctx.homeDir || os.homedir() },
208
+ });
209
+ if (result.error) throw result.error;
210
+ if (result.status !== 0) {
211
+ throw new Error((result.stderr || result.stdout || '').trim() || `zcode ${args.join(' ')} failed`);
212
+ }
213
+ return result.stdout;
214
+ }
215
+
216
+ function nativePlugin(ctx) {
217
+ const value = JSON.parse(zcodeCommand(ctx, ['plugins', 'list', '--json']));
218
+ if (!Array.isArray(value.plugins)) throw new Error('zcode plugins list --json must include plugins');
219
+ return value.plugins.find((entry) => entry?.id === INLINE_PLUGIN_ID) || null;
220
+ }
221
+
222
+ function install(ctx = {}) {
223
+ assertPluginScope(ctx);
224
+ const target = resolveMarketplaceRoot(ctx);
225
+ const pluginRoot = resolvePluginRoot(ctx);
226
+ const configRoot = resolveTarget(ctx);
227
+ const configFile = resolveConfigFile(ctx);
228
+ const repoRoot = resolveRepoRoot(ctx);
229
+ if (fs.existsSync(target) && !isManaged(target)) {
230
+ throw new Error(`refusing to replace unmanaged ZCode marketplace: ${target}`);
231
+ }
232
+ const previousMetadata = managedMetadata(target) || {};
233
+ const cleanupAbsent = previousMetadata.cleanup_absent || captureAbsent(configRoot, [
234
+ '.', 'cli', path.join('cli', 'plugins'), path.join('cli', 'plugins', 'marketplaces'),
235
+ path.join('cli', 'config.json'),
236
+ ]);
237
+ const ownership = previousMetadata.config_ownership || configOwnership(configFile, pluginRoot);
238
+ const configExisted = fs.existsSync(configFile);
239
+ const configBefore = configExisted ? fs.readFileSync(configFile, 'utf8') : null;
240
+ const nextConfig = registeredConfig(loadConfig(configFile), pluginRoot);
241
+ ensureDir(path.dirname(target));
242
+ const staging = fs.mkdtempSync(path.join(path.dirname(target), `.${PLUGIN_NAME}-staging-`));
243
+ const recovery = fs.existsSync(target)
244
+ ? fs.mkdtempSync(path.join(path.dirname(target), `.${PLUGIN_NAME}-recovery-`))
245
+ : null;
246
+ if (recovery) fs.cpSync(target, recovery, { recursive: true });
247
+ try {
248
+ const copied = buildStaging(repoRoot, staging, pluginRoot);
249
+ markManaged(staging, {
250
+ adapter: 'zcode', plugin: PLUGIN_NAME,
251
+ cleanup_absent: cleanupAbsent,
252
+ config_ownership: ownership,
253
+ });
254
+ publishManagedTrees([{ source: staging, target, label: 'ZCode marketplace' }]);
255
+ writeAtomic(configFile, `${JSON.stringify(nextConfig, null, 2)}\n`);
256
+ if (shouldRunHostCli(ctx)) {
257
+ const plugin = nativePlugin(ctx);
258
+ if (!plugin || plugin.enabled !== true || path.resolve(plugin.rootPath) !== path.resolve(pluginRoot)) {
259
+ throw new Error('ZCode did not activate the managed Ultra inline plugin');
260
+ }
261
+ }
262
+ if (recovery) removeTree(recovery);
263
+ return {
264
+ target,
265
+ marketplaceRoot: target,
266
+ marketplaceFile: resolveMarketplaceFile(ctx),
267
+ pluginRoot,
268
+ skillRoot: path.join(pluginRoot, 'skills'),
269
+ hookRoot: path.join(pluginRoot, 'hooks'),
270
+ configFile,
271
+ copied,
272
+ };
273
+ } catch (error) {
274
+ const rollback = [];
275
+ try {
276
+ if (configExisted) writeAtomic(configFile, configBefore);
277
+ else if (fs.existsSync(configFile)) fs.unlinkSync(configFile);
278
+ } catch (rollbackError) { rollback.push(rollbackError); }
279
+ try {
280
+ if (fs.existsSync(target)) removeTree(target);
281
+ if (recovery) fs.renameSync(recovery, target);
282
+ } catch (rollbackError) { rollback.push(rollbackError); }
283
+ if (fs.existsSync(staging)) removeTree(staging);
284
+ pruneCreatedEmpty(configRoot, cleanupAbsent);
285
+ if (rollback.length) throw new AggregateError([error, ...rollback], 'ZCode install and rollback failed');
286
+ throw error;
287
+ }
288
+ }
289
+
290
+ function doctor(ctx = {}) {
291
+ assertPluginScope(ctx);
292
+ const pluginRoot = resolvePluginRoot(ctx);
293
+ const report = inspectPlugin({
294
+ adapter: 'zcode', repoRoot: resolveRepoRoot(ctx), pluginRoot,
295
+ skillRoot: path.join(pluginRoot, 'skills'), hookRoot: path.join(pluginRoot, 'hooks'),
296
+ manifestFile: path.join(pluginRoot, '.zcode-plugin', 'plugin.json'),
297
+ });
298
+ let marketplaceValid = false;
299
+ try {
300
+ const marketplace = JSON.parse(fs.readFileSync(resolveMarketplaceFile(ctx), 'utf8'));
301
+ marketplaceValid = marketplace?.name === PLUGIN_NAME
302
+ && Array.isArray(marketplace.plugins)
303
+ && marketplace.plugins.some((entry) => entry?.name === PLUGIN_NAME && entry?.source === './plugin');
304
+ } catch {}
305
+ report.checks.marketplace = { status: marketplaceValid ? 'pass' : 'fail' };
306
+ if (!marketplaceValid) report.issues.push({ code: 'ZCODE_MARKETPLACE_INVALID' });
307
+
308
+ let registered = false;
309
+ try {
310
+ const config = loadConfig(resolveConfigFile(ctx));
311
+ registered = config.plugins?.enabled !== false
312
+ && Array.isArray(config.plugins?.dirs)
313
+ && config.plugins.dirs.includes(pluginRoot);
314
+ } catch {}
315
+ report.checks.registration = { status: registered ? 'pass' : 'fail' };
316
+ if (!registered) report.issues.push({ code: 'ZCODE_PLUGIN_NOT_REGISTERED' });
317
+
318
+ if (shouldRunHostCli(ctx)) {
319
+ let discovered = false;
320
+ try {
321
+ const plugin = nativePlugin(ctx);
322
+ discovered = !!plugin && plugin.enabled === true
323
+ && path.resolve(plugin.rootPath) === path.resolve(pluginRoot)
324
+ && plugin.skillCount === 14;
325
+ } catch {}
326
+ report.checks.native_registry = { status: discovered ? 'pass' : 'fail' };
327
+ if (!discovered) report.issues.push({ code: 'ZCODE_NATIVE_DISCOVERY_FAILED' });
328
+ }
329
+ report.status = report.issues.length === 0 ? 'healthy' : 'degraded';
330
+ return report;
331
+ }
332
+
333
+ function uninstall(ctx = {}) {
334
+ assertPluginScope(ctx);
335
+ const target = resolveMarketplaceRoot(ctx);
336
+ const pluginRoot = resolvePluginRoot(ctx);
337
+ const configRoot = resolveTarget(ctx);
338
+ const metadata = managedMetadata(target) || {};
339
+ const ownership = metadata.config_ownership;
340
+ const cleanupAbsent = metadata.cleanup_absent || [];
341
+ if (fs.existsSync(target) && !ownership) {
342
+ throw new Error(`missing ZCode config ownership in ${target}`);
343
+ }
344
+ const configFile = resolveConfigFile(ctx);
345
+ const configBefore = fs.existsSync(configFile) ? fs.readFileSync(configFile, 'utf8') : null;
346
+ if (ownership) {
347
+ const next = unregisteredConfig(loadConfig(configFile), pluginRoot, ownership);
348
+ if (ownership.file_created && Object.keys(next).length === 0) fs.unlinkSync(configFile);
349
+ else writeAtomic(configFile, `${JSON.stringify(next, null, 2)}\n`);
350
+ }
351
+ try {
352
+ const removed = removeManaged(target, 'ZCode marketplace');
353
+ const cleaned = pruneCreatedEmpty(configRoot, cleanupAbsent);
354
+ return { target, marketplaceRoot: target, pluginRoot, removed: { marketplace: removed }, cleaned };
355
+ } catch (error) {
356
+ if (configBefore !== null) writeAtomic(configFile, configBefore);
357
+ throw error;
358
+ }
359
+ }
360
+
361
+ module.exports = {
362
+ name: 'zcode',
363
+ PLUGIN_NAME,
364
+ INLINE_PLUGIN_ID,
365
+ buildHooksManifest,
366
+ resolveTarget,
367
+ resolveCliRoot,
368
+ resolveConfigFile,
369
+ resolveMarketplaceRoot,
370
+ resolveMarketplaceFile,
371
+ resolvePluginRoot,
372
+ install,
373
+ doctor,
374
+ uninstall,
375
+ };
@@ -70,11 +70,24 @@ function parseJson(value) {
70
70
  try { return JSON.parse(text); } catch { return null; }
71
71
  }
72
72
 
73
+ function parseFencedJson(value) {
74
+ if (typeof value !== 'string') return [];
75
+ const parsed = [];
76
+ const fences = /```(?:json)?\s*([\s\S]*?)\s*```/gi;
77
+ for (const match of value.matchAll(fences)) {
78
+ try { parsed.push(JSON.parse(match[1])); } catch {}
79
+ }
80
+ return parsed;
81
+ }
82
+
73
83
  function collectResultCandidates(value, candidates, depth = 0) {
74
84
  if (depth > 12 || value === null || value === undefined) return;
75
85
  if (typeof value === 'string') {
76
86
  const parsed = parseJson(value);
77
87
  if (parsed !== null) collectResultCandidates(parsed, candidates, depth + 1);
88
+ for (const fenced of parseFencedJson(value)) {
89
+ collectResultCandidates(fenced, candidates, depth + 1);
90
+ }
78
91
  return;
79
92
  }
80
93
  if (Array.isArray(value)) {
@@ -93,6 +106,7 @@ function extractModelResult(spec) {
93
106
  for (const file of [spec.host_output, spec.stdout]) {
94
107
  if (!file || !fs.existsSync(file) || fs.statSync(file).size === 0) continue;
95
108
  const text = fs.readFileSync(file, 'utf8');
109
+ collectResultCandidates(text, candidates);
96
110
  const full = parseJson(text);
97
111
  if (full !== null) collectResultCandidates(full, candidates);
98
112
  for (const line of text.split('\n')) {
@@ -167,6 +181,12 @@ function mechanical(spec, exitCode, signal) {
167
181
  return {
168
182
  delegation_id: spec.delegation_id,
169
183
  host: spec.host,
184
+ transport_maturity: spec.transport_maturity ?? null,
185
+ transport_surface: spec.transport_surface ?? null,
186
+ // The explicit experimental acknowledgment travels to every terminal
187
+ // result — finished, failed, cancelled, or interrupted — so no result can
188
+ // imply an undocumented surface was silently launched.
189
+ ...(spec.experimental_ack === true ? { experimental_ack: true } : {}),
170
190
  instruction_digest: spec.instruction_digest,
171
191
  permission_digest: spec.permission_digest,
172
192
  output_schema_digest: spec.output_schema_digest,
package/bin/delegate.cjs CHANGED
@@ -5,7 +5,7 @@ const fs = require('node:fs');
5
5
  const path = require('node:path');
6
6
  const { execFileSync, spawn } = require('node:child_process');
7
7
 
8
- const { hostProfile } = require('../adapters/_shared/host-profile.cjs');
8
+ const { hostProfile, transportSurface } = require('../adapters/_shared/host-profile.cjs');
9
9
  const { writeAtomic } = require('../adapters/_shared/file-ops.cjs');
10
10
 
11
11
  const PERMISSION_SCHEMA = 'ultra-delegation-permission-v1';
@@ -21,28 +21,28 @@ const RESULT_JSON_SCHEMA = Object.freeze({
21
21
  'questions', 'residual_risks',
22
22
  ],
23
23
  properties: {
24
- $schema: { const: RESULT_SCHEMA },
25
- status: { enum: ['finished', 'blocked', 'failed'] },
26
- summary: { type: 'string', minLength: 1 },
24
+ $schema: { type: 'string', const: RESULT_SCHEMA },
25
+ status: { type: 'string', enum: ['finished', 'blocked', 'failed'] },
26
+ summary: { type: 'string' },
27
27
  changed_files: {
28
- type: 'array', uniqueItems: true, items: { type: 'string', minLength: 1 },
28
+ type: 'array', items: { type: 'string' },
29
29
  },
30
30
  checks: {
31
31
  type: 'array',
32
32
  items: {
33
33
  type: 'object',
34
34
  additionalProperties: false,
35
- required: ['command', 'status'],
35
+ required: ['command', 'status', 'output_ref'],
36
36
  properties: {
37
- command: { type: 'string', minLength: 1 },
38
- status: { enum: ['passed', 'failed', 'not_run'] },
39
- output_ref: { type: 'string', minLength: 1 },
37
+ command: { type: 'string' },
38
+ status: { type: 'string', enum: ['passed', 'failed', 'not_run'] },
39
+ output_ref: { type: 'string' },
40
40
  },
41
41
  },
42
42
  },
43
- evidence: { type: 'array', items: { type: 'string', minLength: 1 } },
44
- questions: { type: 'array', items: { type: 'string', minLength: 1 } },
45
- residual_risks: { type: 'array', items: { type: 'string', minLength: 1 } },
43
+ evidence: { type: 'array', items: { type: 'string' } },
44
+ questions: { type: 'array', items: { type: 'string' } },
45
+ residual_risks: { type: 'array', items: { type: 'string' } },
46
46
  },
47
47
  });
48
48
 
@@ -115,8 +115,10 @@ function validateWorktree(worktree) {
115
115
 
116
116
  function readPermission(file, worktree) {
117
117
  let payload;
118
+ let source;
118
119
  try {
119
- payload = JSON.parse(fs.readFileSync(file, 'utf8'));
120
+ source = fs.readFileSync(file, 'utf8');
121
+ payload = JSON.parse(source);
120
122
  } catch {
121
123
  throw new Error('permission must be valid JSON');
122
124
  }
@@ -167,24 +169,40 @@ function readPermission(file, worktree) {
167
169
  if (payload.external_effects.length !== 0) {
168
170
  throw new Error('permission external_effects must be empty; the primary host performs separately authorized effects');
169
171
  }
170
- return { writableRoots, writableRootPaths };
172
+ return {
173
+ writableRoots,
174
+ writableRootPaths,
175
+ source,
176
+ digest: crypto.createHash('sha256').update(source).digest('hex'),
177
+ };
171
178
  }
172
179
 
173
- function delegatePrompt({ instruction, permission, worktree, readOnly }) {
180
+ function delegatePrompt({
181
+ instructionSource, instructionDigest, permissionSource, permissionDigest,
182
+ worktree, readOnly,
183
+ }) {
174
184
  return [
175
185
  'Execute one bounded Ultra Builder Pro delegation in the isolated Git worktree.',
176
- `Read the immutable instruction file: ${instruction}`,
177
- `Read the immutable permission file: ${permission}`,
186
+ 'The launcher embedded the digest-bound packet below. Do not access packet paths outside the worktree.',
187
+ `Instruction SHA-256: ${instructionDigest}`,
188
+ '--- instruction.md ---',
189
+ instructionSource.trimEnd(),
190
+ '--- end instruction.md ---',
191
+ `Permission SHA-256: ${permissionDigest}`,
192
+ '--- permission.json ---',
193
+ permissionSource.trimEnd(),
194
+ '--- end permission.json ---',
178
195
  `Use this worktree as the only filesystem boundary: ${worktree}`,
179
196
  readOnly
180
197
  ? 'This is read-only delegation. Do not create, modify, or delete any project file.'
181
198
  : 'Modify only paths covered by permission.writable_roots.',
182
199
  'Do not perform external effects, edit the primary checkout, or change Ultra task authority.',
200
+ 'Batch independent reads in the earliest tool turn. Do not emit a preliminary result, and reserve a final turn for the one terminal JSON response.',
183
201
  `Return exactly one JSON object as the final response using $schema ${RESULT_SCHEMA}.`,
184
- 'The exact fields are status, summary, changed_files, checks, evidence, questions, and residual_risks.',
202
+ 'The exact fields are $schema, status, summary, changed_files, checks, evidence, questions, and residual_risks.',
185
203
  'status is finished, blocked, or failed. blocked requires at least one question.',
186
204
  'changed_files must exactly list every repository-relative path changed in the worktree.',
187
- 'checks items use command, status (passed, failed, or not_run), and optional output_ref.',
205
+ 'checks items use command, status (passed, failed, or not_run), and output_ref. Use a short evidence reference when no file exists.',
188
206
  'Do not write the receipt yourself. The launcher validates structured output, process exit, permissions, and the actual Git diff before publication.',
189
207
  ].join('\n');
190
208
  }
@@ -193,6 +211,10 @@ function selectedBinary(runtime, fallback) {
193
211
  return process.env[`UBP_DELEGATE_${runtime.toUpperCase()}_BIN`] || fallback;
194
212
  }
195
213
 
214
+ function selectedModel(runtime, explicit) {
215
+ return explicit || process.env[`UBP_DELEGATE_${runtime.toUpperCase()}_MODEL`] || null;
216
+ }
217
+
196
218
  function parsePairs(argv, allowed) {
197
219
  const values = {};
198
220
  for (let index = 0; index < argv.length; index += 2) {
@@ -208,8 +230,12 @@ function parsePairs(argv, allowed) {
208
230
  }
209
231
 
210
232
  function parseRun(argv) {
211
- const values = parsePairs(argv, new Set([
212
- '--to', '--instruction', '--permission', '--worktree', '--timeout',
233
+ // --ack-experimental is the one valueless flag: launching an experimental
234
+ // transport must be a visible choice, never a silent default.
235
+ const ackExperimental = argv.includes('--ack-experimental');
236
+ const pairs = argv.filter((arg) => arg !== '--ack-experimental');
237
+ const values = parsePairs(pairs, new Set([
238
+ '--to', '--instruction', '--permission', '--worktree', '--timeout', '--model',
213
239
  ]));
214
240
  for (const key of ['--to', '--instruction', '--permission', '--worktree']) {
215
241
  if (!values[key]) throw new Error(`delegate run requires ${key}`);
@@ -221,7 +247,9 @@ function parseRun(argv) {
221
247
  return {
222
248
  to: values['--to'], instruction: values['--instruction'],
223
249
  permission: values['--permission'], worktree: values['--worktree'],
250
+ model: values['--model'] || null,
224
251
  timeoutMs: Math.ceil(timeout * 1000),
252
+ ackExperimental,
225
253
  };
226
254
  }
227
255
 
@@ -238,6 +266,28 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
238
266
  const args = parseRun(argv);
239
267
  projectRoot = fs.realpathSync(projectRoot);
240
268
  const profile = hostProfile(args.to);
269
+ if (args.model && !profile.supportsModelSelection) {
270
+ throw new Error(`delegate --model is not supported by host ${args.to}`);
271
+ }
272
+ const model = profile.supportsModelSelection ? selectedModel(args.to, args.model) : null;
273
+ const binary = selectedBinary(args.to, profile.binary);
274
+ const transportMaturity = profile.transportMaturity;
275
+ const transportSurfaceNote = transportSurface(args.to, binary);
276
+ if (transportMaturity === 'experimental' && !args.ackExperimental) {
277
+ throw new Error(
278
+ `delegate --to ${args.to} would launch ${transportSurfaceNote}. `
279
+ + 'This transport is experimental (verified-local only; the provider publishes no '
280
+ + 'stability contract), and it is not the documented ZCode Desktop interactive '
281
+ + 'surface. Acknowledge explicitly with --ack-experimental to proceed, or pick a '
282
+ + 'documented transport.',
283
+ );
284
+ }
285
+ if (transportMaturity === 'experimental') {
286
+ process.stderr.write(
287
+ `ultra-delegate: warning: host '${args.to}' runs ${transportSurfaceNote}; `
288
+ + `recovery: ubp delegate cancel --delegation <dir>\n`,
289
+ );
290
+ }
241
291
  const instruction = projectPath(projectRoot, args.instruction, 'instruction');
242
292
  const permission = projectPath(projectRoot, args.permission, 'permission');
243
293
  const worktree = projectPath(projectRoot, args.worktree, 'worktree', { directory: true });
@@ -248,6 +298,7 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
248
298
  }
249
299
  const baseHead = validateWorktree(worktree);
250
300
  const permissionContract = readPermission(permission, worktree);
301
+ const instructionSource = fs.readFileSync(instruction, 'utf8');
251
302
  const delegationRoot = path.dirname(instruction);
252
303
  const delegationId = path.basename(delegationRoot);
253
304
  const result = path.join(delegationRoot, 'result.json');
@@ -272,8 +323,8 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
272
323
  throw error;
273
324
  }
274
325
  const startedAt = new Date().toISOString();
275
- const instructionDigest = sha256(instruction);
276
- const permissionDigest = sha256(permission);
326
+ const instructionDigest = crypto.createHash('sha256').update(instructionSource).digest('hex');
327
+ const permissionDigest = permissionContract.digest;
277
328
  try {
278
329
  fs.writeFileSync(lockFd, `${JSON.stringify({ pid: process.pid, started_at: startedAt })}\n`);
279
330
  fs.closeSync(lockFd);
@@ -287,15 +338,25 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
287
338
  schemaFile: outputSchema,
288
339
  schemaJson: JSON.stringify(RESULT_JSON_SCHEMA),
289
340
  hostOutput,
341
+ model,
290
342
  };
291
343
  const prompt = delegatePrompt({
292
- instruction, permission, worktree, readOnly: profileOptions.readOnly,
344
+ instructionSource,
345
+ instructionDigest,
346
+ permissionSource: permissionContract.source,
347
+ permissionDigest,
348
+ worktree,
349
+ readOnly: profileOptions.readOnly,
293
350
  });
294
351
  const workerSpec = {
295
352
  $schema: 'ultra-delegation-worker-spec-v1',
296
353
  delegation_id: delegationId,
297
354
  host: args.to,
298
- command: selectedBinary(args.to, profile.binary),
355
+ model,
356
+ command: binary,
357
+ transport_maturity: transportMaturity,
358
+ transport_surface: transportSurfaceNote,
359
+ experimental_ack: transportMaturity === 'experimental' ? args.ackExperimental : undefined,
299
360
  args: profile.delegateArgv(prompt, worktree, profileOptions),
300
361
  cwd: worktree,
301
362
  instruction,
@@ -341,6 +402,10 @@ function run(argv, { projectRoot = process.cwd() } = {}) {
341
402
  status: 'started',
342
403
  delegation_id: delegationId,
343
404
  host: args.to,
405
+ model,
406
+ transport_maturity: transportMaturity,
407
+ transport_surface: transportSurfaceNote,
408
+ ...(transportMaturity === 'experimental' ? { experimental_ack: args.ackExperimental } : {}),
344
409
  worker_pid: worker.pid,
345
410
  instruction_digest: instructionDigest,
346
411
  permission_digest: permissionDigest,
@@ -415,6 +480,10 @@ function failureFromSpec(spec, failureType, summary) {
415
480
  failure_type: failureType,
416
481
  delegation_id: spec.delegation_id,
417
482
  host: spec.host,
483
+ transport_maturity: spec.transport_maturity ?? null,
484
+ transport_surface: spec.transport_surface ?? null,
485
+ // Terminal recovery results carry the same transport truth as live ones.
486
+ ...(spec.experimental_ack === true ? { experimental_ack: true } : {}),
418
487
  instruction_digest: spec.instruction_digest,
419
488
  permission_digest: spec.permission_digest,
420
489
  output_schema_digest: spec.output_schema_digest,