ultra-builder-pro-cli 0.18.0 → 0.20.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 (44) hide show
  1. package/CHANGELOG.md +54 -1
  2. package/README.md +16 -27
  3. package/adapters/_shared/interaction-contract.cjs +26 -4
  4. package/adapters/claude.js +3 -1
  5. package/adapters/kimi.js +4 -30
  6. package/adapters/opencode.js +50 -14
  7. package/commands/ultra-research.md +1 -1
  8. package/docs/AGENT-CONTEXT.md +23 -34
  9. package/docs/ARCHITECTURE.md +26 -12
  10. package/docs/DECISIONS.md +12 -8
  11. package/docs/LEGACY-CLI-CROSSWALK.md +9 -7
  12. package/docs/PLUGIN-ISOLATION-CONTRACT.md +54 -0
  13. package/docs/ROADMAP.md +5 -5
  14. package/docs/RUNTIME-COMPAT-MATRIX.md +26 -25
  15. package/docs/STATE-DB-ACCESS-POLICY.md +1 -1
  16. package/docs/WORKFLOW-LIFECYCLE.md +45 -25
  17. package/hooks/active_task_context.py +1 -1
  18. package/hooks/context_spine.py +1 -1
  19. package/hooks/health_check.py +19 -0
  20. package/hooks/pre_stop_check.py +1 -1
  21. package/hooks/workflow_checkpoint.py +1 -1
  22. package/hooks/workflow_context.py +1 -1
  23. package/hooks/workflow_resume.py +1 -1
  24. package/mcp-server/lib/decision-dialogue.cjs +84 -11
  25. package/mcp-server/lib/doctor.cjs +2 -1
  26. package/mcp-server/lib/state-db.cjs +143 -13
  27. package/mcp-server/lib/workflow-state.cjs +4 -4
  28. package/mcp-server/server.cjs +24 -8
  29. package/package.json +3 -4
  30. package/skills/ultra-change/SKILL.md +16 -8
  31. package/skills/ultra-init/SKILL.md +2 -1
  32. package/skills/ultra-plan/SKILL.md +20 -3
  33. package/skills/ultra-research/SKILL.md +25 -15
  34. package/skills/ultra-test/SKILL.md +5 -2
  35. package/skills/ultra-think/SKILL.md +2 -0
  36. package/skills/ultra-think/references/decision-dialogue.md +47 -11
  37. package/spec/cli-protocol.md +1 -0
  38. package/spec/mcp-tools.yaml +30 -3
  39. package/spec/schemas/state-db.sql +7 -4
  40. package/ultra-tools/cli.cjs +1 -1
  41. package/ultra-tools/commands/status.cjs +2 -1
  42. package/adapters/_shared/handbook.cjs +0 -569
  43. package/bin/handbook.js +0 -107
  44. package/docs/USER-HANDBOOK-CONTRACT.md +0 -120
package/CHANGELOG.md CHANGED
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.20.0] — 2026-07-28
11
+
12
+ ### Added
13
+
14
+ - Added schema 19 and `decision.complete` so settled normalized intent reaches a
15
+ terminal lifecycle state without fabricating a second user approval or an
16
+ artifact checkpoint. Existing settled schema-18 threads migrate safely.
17
+
18
+ ### Changed
19
+
20
+ - Unified semantic selection across Claude Code, Codex, OpenCode, and Kimi Code
21
+ as `inspect -> suggest -> ask if unresolved -> normalize -> persist`, using
22
+ each host's native structured question surface with a direct-question
23
+ fallback.
24
+ - Made research coverage and planning posture owner-selected after model
25
+ recommendation while preserving autonomous fact finding, synthesis,
26
+ decomposition, and reversible implementation judgment.
27
+ - Stopped requiring a ceremonial decision checkpoint after every normalized
28
+ answer. Artifact checkpoints remain explicit digest-bound freshness gates.
29
+ - Made unanswered non-blocking follow-ups advisory instead of global workflow
30
+ gates; unanswered blocking choices and blocking deferrals still fail closed.
31
+
32
+ ## [0.19.1] — 2026-07-26
33
+
34
+ ### Fixed
35
+
36
+ - Made the four-host handbook-isolation release test hermetic so a clean CI
37
+ runner does not require the Codex CLI merely to prove that installation and
38
+ uninstallation preserve user-owned instruction files byte for byte.
39
+
40
+ ## [0.19.0] — 2026-07-26
41
+
42
+ ### Changed
43
+
44
+ - Made every public Ultra workflow explicitly activated. Claude Code and Kimi
45
+ Code disable model invocation, Codex keeps implicit invocation disabled, and
46
+ OpenCode commands load private plugin workflow assets instead of exposing
47
+ public workflows through its model skill catalog.
48
+ - Limited lifecycle context, health, checkpoint, stop, and subagent hooks to
49
+ DB-authoritative active workflows. Initialized idle projects retain only
50
+ generated-projection protection.
51
+ - Changed workflow handoff to return allowed transitions and a host-owned
52
+ recommendation without launching another public workflow.
53
+
54
+ ### Removed
55
+
56
+ - Removed `ubp-handbook`, the shared handbook renderer, Kimi session bootstrap,
57
+ and every package path capable of creating or rewriting user-level
58
+ `CLAUDE.md` or `AGENTS.md` files.
59
+
10
60
  ## [0.18.0] — 2026-07-25
11
61
 
12
62
  ### Added
@@ -789,7 +839,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
789
839
  skill manifest, CLI protocol + mapping table; 5 spec validators.
790
840
  - **Phase 0 — skeleton**: multi-runtime installer scaffolding.
791
841
 
792
- [Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.18.0...HEAD
842
+ [Unreleased]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.20.0...HEAD
843
+ [0.20.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.19.1...v0.20.0
844
+ [0.19.1]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.19.0...v0.19.1
845
+ [0.19.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.18.0...v0.19.0
793
846
  [0.18.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.17.1...v0.18.0
794
847
  [0.17.1]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.17.0...v0.17.1
795
848
  [0.17.0]: https://github.com/rocky2431/ultra-builder-pro-cli/compare/v0.16.0...v0.17.0
package/README.md CHANGED
@@ -73,8 +73,8 @@ The responsibility split is deliberate:
73
73
 
74
74
  | Owner | Responsibility |
75
75
  |---|---|
76
- | **User** | Product intent, material scope and trade-offs, destructive actions, publishing and deployment authorization |
77
- | **Host model** | Fact-finding, synthesis, research coverage, route recommendation, reversible implementation decisions |
76
+ | **User** | Product intent, semantic route selection, material scope and trade-offs, risk acceptance, destructive actions, publishing and deployment authorization |
77
+ | **Host model** | Fact-finding, synthesis, research-coverage and route recommendations, reversible implementation decisions |
78
78
  | **Ultra MCP** | Durable state, evidence references, digests, freshness, locks, valid transitions and hard recovery |
79
79
  | **Host adapter** | Native Skill discovery, user questions, tool invocation, installation, and runtime wiring |
80
80
  | **Hooks** | Fast lifecycle observation, current breadcrumb injection, and protection of generated projections |
@@ -155,24 +155,11 @@ npx --yes ultra-builder-pro-cli@latest --all --global --doctor --json
155
155
  After installing or upgrading, start a new Claude Code, OpenCode, or Codex
156
156
  session. In Kimi Code, run `/reload` or start a new session.
157
157
 
158
- Plugin installation does not mutate durable user instructions. To preview and
159
- explicitly converge a legacy handbook into the complete host-native engineering
160
- contract, use full mode:
161
-
162
- ```bash
163
- npx --yes --package ultra-builder-pro-cli@latest \
164
- ubp-handbook preview --runtime codex --full > /tmp/ubp-codex-handbook.md
165
- # Inspect the preview, then copy the confirmation token printed to the terminal.
166
- npx --yes --package ultra-builder-pro-cli@latest \
167
- ubp-handbook apply --runtime codex --full \
168
- --confirm "PASTE_64_CHARACTER_TOKEN_HERE"
169
- ```
170
-
171
- Replace `codex` with `claude`, `opencode`, or `kimi` for the other hosts. The
172
- apply command rejects a missing or stale preview token, creates a timestamped
173
- backup, follows user-managed handbook symlinks, and preserves supported
174
- marker-delimited content owned by other providers. Without `--full`, only the
175
- small Ultra runtime block is merged.
158
+ Plugin installation, update, doctor, and uninstall never mutate durable user
159
+ instructions such as `CLAUDE.md` or `AGENTS.md`. Ultra policy stays in the plugin
160
+ and becomes active only after an explicit workflow invocation. Existing legacy
161
+ Ultra marker blocks in a user handbook are left untouched for the owner to review;
162
+ the plugin neither claims nor silently deletes user-authored content.
176
163
 
177
164
  ### Host invocation
178
165
 
@@ -202,7 +189,7 @@ Initialization:
202
189
  - identifies the repository root and scope;
203
190
  - classifies the repository as greenfield, brownfield, or migrated;
204
191
  - initializes Git when needed;
205
- - creates `.ultra/` and schema 18 project authority;
192
+ - creates `.ultra/` and schema 19 project authority;
206
193
  - verifies that the scaffold and database can be read back;
207
194
  - completes without silently starting research, creating a commit, adding a
208
195
  remote, or pushing anything.
@@ -244,9 +231,11 @@ product story. It builds a current-system baseline from:
244
231
  - APIs, data, permissions, integrations, deployment, and recovery seams;
245
232
  - known failures, documentation drift, technical debt, and unresolved unknowns.
246
233
 
247
- During `ultra-research`, the host model selects the smallest evidence-backed set
248
- of applicable catalog areas. Every included area receives one explicit
249
- disposition:
234
+ During `ultra-research`, the host model inspects current evidence and recommends
235
+ the smallest sufficient route. The owner selects, modifies, delegates, or
236
+ defers it through the host-native question surface unless the current request
237
+ already resolves the route. The normalized accepted coverage is then stored in
238
+ `.ultra/state.db`. Every included area receives one explicit disposition:
250
239
 
251
240
  - `execute` — produce fresh evidence;
252
241
  - `verify_existing` — validate an existing artifact;
@@ -256,7 +245,8 @@ disposition:
256
245
 
257
246
  The catalog is not a mandatory document set or questionnaire. Omitted areas
258
247
  create no workflow rows; an explicit exclusion is recorded only when retaining
259
- that rationale is useful.
248
+ that rationale is useful. MCP validates state, evidence, and transitions; it
249
+ does not store or prove the preceding UI interaction.
260
250
 
261
251
  Older projection-only Ultra projects are preserved and routed through a
262
252
  backup-first migration or rebaseline. Use `ultra-doctor` when initialization
@@ -366,7 +356,6 @@ worker execution or verified auto-merge.
366
356
  | `ultra-builder-pro-cli` / `ubp` | Install, update, uninstall, and diagnose host plugins |
367
357
  | `ultra-tools` | Inspect and maintain project tasks, sessions, state, migration, and recovery |
368
358
  | `ubp-orchestrator` | Execute current dependency waves or supervise configured workers |
369
- | `ubp-handbook` | Preview or apply either the bounded Ultra block or the explicit full host-native handbook |
370
359
 
371
360
  Useful read-only checks:
372
361
 
@@ -426,7 +415,7 @@ and `test:rest`.
426
415
  | [Runtime Compatibility Matrix](./docs/RUNTIME-COMPAT-MATRIX.md) | Claude Code, OpenCode, Codex, and Kimi presentation details |
427
416
  | [Legacy CLI Crosswalk](./docs/LEGACY-CLI-CROSSWALK.md) | What was preserved, strengthened, or replaced from the original Ultra Builder Pro |
428
417
  | [Agent Context](./docs/AGENT-CONTEXT.md) | Context Manifest and host-agent execution contract |
429
- | [User Handbook Contract](./docs/USER-HANDBOOK-CONTRACT.md) | Shared user-level policy and managed host renderings |
418
+ | [Plugin Isolation Contract](./docs/PLUGIN-ISOLATION-CONTRACT.md) | Plugin ownership, explicit activation, idle behavior, and user-instruction isolation |
430
419
  | [State DB Access Policy](./docs/STATE-DB-ACCESS-POLICY.md) | Multi-process authority and write rules |
431
420
  | [Roadmap](./docs/ROADMAP.md) | Current and historical delivery scope |
432
421
  | [Changelog](./CHANGELOG.md) | Release history |
@@ -4,18 +4,22 @@ const SURFACES = Object.freeze({
4
4
  claude: Object.freeze({
5
5
  primary: 'AskUserQuestion',
6
6
  fallback: 'direct_user_interaction',
7
+ availability: 'interactive_session',
7
8
  }),
8
9
  codex: Object.freeze({
9
10
  primary: 'request_user_input',
10
11
  fallback: 'direct_user_interaction',
12
+ availability: 'current_mode_exposes_tool',
11
13
  }),
12
14
  opencode: Object.freeze({
13
15
  primary: 'question',
14
16
  fallback: 'direct_user_interaction',
17
+ availability: 'question_permission_not_denied',
15
18
  }),
16
19
  kimi: Object.freeze({
17
- primary: 'native_question_surface',
20
+ primary: 'AskUserQuestion',
18
21
  fallback: 'direct_user_interaction',
22
+ availability: 'interactive_non_auto_mode',
19
23
  }),
20
24
  });
21
25
 
@@ -23,18 +27,21 @@ function interactionContract(runtime) {
23
27
  const question = SURFACES[runtime];
24
28
  if (!question) throw new Error(`unsupported interaction runtime: ${runtime}`);
25
29
  return {
26
- schema_version: '1.0',
30
+ schema_version: '1.1',
27
31
  runtime,
28
32
  authority: {
29
33
  user: [
30
34
  'product_intent',
31
35
  'material_scope_and_tradeoffs',
36
+ 'semantic_route_selection',
37
+ 'risk_acceptance',
32
38
  'destructive_or_external_effect_authorization',
33
39
  ],
34
40
  host_model: [
35
41
  'fact_finding_and_synthesis',
36
42
  'reversible_implementation_judgment',
37
- 'coverage_risk_and_route_recommendation',
43
+ 'semantic_route_recommendation',
44
+ 'accepted_intent_normalization',
38
45
  ],
39
46
  ultra_mcp: [
40
47
  'durable_state_and_evidence_refs',
@@ -44,10 +51,25 @@ function interactionContract(runtime) {
44
51
  },
45
52
  interaction: {
46
53
  question_surface: question,
54
+ semantic_selection_flow: [
55
+ 'inspect',
56
+ 'suggest',
57
+ 'ask_if_unresolved',
58
+ 'normalize',
59
+ 'persist',
60
+ ],
47
61
  dependent_decisions: 'one_at_a_time',
48
62
  independent_low_load_questions: { maximum: 3 },
49
63
  explicit_current_intent: 'normalize_without_reconfirmation',
50
- unavailable_native_surface: 'use_direct_interaction',
64
+ dismissed_question: 'remain_unanswered',
65
+ unavailable_native_surface: 'use_direct_interaction_if_permitted',
66
+ host_forbids_interaction: 'remain_unanswered',
67
+ },
68
+ persistence: {
69
+ accepted_intent: 'trust_as_current_authority',
70
+ user_interaction_proof: 'not_required',
71
+ raw_prompt_or_transcript: 'never_store',
72
+ pending_question: 'store_only_when_recovery_requires_it',
51
73
  },
52
74
  routing: {
53
75
  semantic_recommendation_owner: 'host_model',
@@ -68,10 +68,12 @@ function copySkills(repoRoot, target, names) {
68
68
  'Claude Code Task workers using the installed review agent definitions',
69
69
  );
70
70
  }
71
+ const policy = skillPolicy(name);
71
72
  return Buffer.from(serializeFrontmatter({
72
73
  name,
73
74
  description: fm.description,
74
- 'user-invocable': skillPolicy(name).userInvocable,
75
+ 'user-invocable': policy.userInvocable,
76
+ ...(policy.userInvocable ? { 'disable-model-invocation': true } : {}),
75
77
  }, adaptedBody));
76
78
  },
77
79
  });
package/adapters/kimi.js CHANGED
@@ -20,6 +20,7 @@ const provenance = require('./_shared/provenance.cjs');
20
20
  const {
21
21
  CORE_PUBLIC_SKILLS,
22
22
  WORKFLOW_HOOK_FILES,
23
+ skillPolicy,
23
24
  skillsForRuntime,
24
25
  } = require('./_shared/runtime-assets.cjs');
25
26
 
@@ -28,7 +29,6 @@ const MCP_SERVER_NAME = PLUGIN_ID;
28
29
  const PROVENANCE_FILE = 'provenance.json';
29
30
  const REGISTRY_RELATIVE = path.join('plugins', 'installed.json');
30
31
  const MANAGED_RELATIVE = path.join('plugins', 'managed', PLUGIN_ID);
31
- const BOOTSTRAP_SKILL = 'using-ultra-builder-pro';
32
32
 
33
33
  function resolveTarget(ctx = {}) {
34
34
  if (ctx.configDir) return path.resolve(ctx.configDir);
@@ -127,11 +127,13 @@ function transformSkill(buf, relPath, skillName) {
127
127
  if (!relPath.endsWith('.md')) return buf;
128
128
  const { fm, body } = parseFm(source);
129
129
  if (!fm) return Buffer.from(kimiTextTransform(source, skillName), 'utf8');
130
+ const policy = skillPolicy(skillName);
130
131
  const nativeFm = {
131
132
  name: String(fm.name || skillName),
132
133
  description: kimiTextTransform(fm.description || `${skillName} workflow`, skillName)
133
134
  .replace(/\s+/g, ' ')
134
135
  .trim(),
136
+ ...(policy.userInvocable ? { disableModelInvocation: true } : {}),
135
137
  };
136
138
  return Buffer.from(serializeFm(nativeFm, kimiTextTransform(body, skillName)), 'utf8');
137
139
  }
@@ -162,29 +164,6 @@ function copySkills(repoRoot, pluginRoot) {
162
164
  });
163
165
  installed.push(name);
164
166
  }
165
- const bootstrapRoot = path.join(pluginRoot, 'skills', BOOTSTRAP_SKILL);
166
- ensureDir(bootstrapRoot);
167
- writeAtomic(path.join(bootstrapRoot, 'SKILL.md'), `---
168
- name: ${BOOTSTRAP_SKILL}
169
- description: Bootstrap the Kimi-native Ultra Builder Pro runtime contract at session start.
170
- ---
171
-
172
- # Ultra Builder Pro for Kimi Code
173
-
174
- Kimi Code is the primary host and owns all edits, evidence, and final verification. Use the
175
- namespaced \`/ultra-builder-pro:ultra-*\` commands or invoke the registered Ultra skills directly.
176
-
177
- - \`.ultra/state.db\` is the only durable Ultra authority. JSON and Markdown under \`.ultra/\` are
178
- projections or workflow artifacts and must never replace MCP state writes.
179
- - Memory and code graph data belong to separately installed external providers. Ultra stores only
180
- provider metadata references and never captures prompts, transcripts, or cross-session memory.
181
- - Use Kimi \`TodoList\` for session coordination and Kimi \`Agent\` / \`AgentSwarm\` for bounded
182
- workers. Bundled files under \`$KIMI_PLUGIN_ROOT/agents/\` are worker prompt templates.
183
- - Before compaction, Ultra saves its workflow checkpoint. After compaction or recovery, inspect
184
- \`.ultra/runtime/checkpoint.json\` and call the Ultra status/doctor MCP tools before continuing.
185
- - If no Ultra workflow is active, the lifecycle hooks remain advisory except projection protection.
186
- `);
187
- installed.push(BOOTSTRAP_SKILL);
188
167
  return installed;
189
168
  }
190
169
 
@@ -280,7 +259,6 @@ function writeManifest(repoRoot, pluginRoot) {
280
259
  homepage: pkg.homepage,
281
260
  license: pkg.license || 'MIT',
282
261
  skills: ['./skills'],
283
- sessionStart: { skill: BOOTSTRAP_SKILL },
284
262
  mcpServers: { [MCP_SERVER_NAME]: mcpManifest() },
285
263
  hooks: buildHooksManifest(),
286
264
  commands: ['./commands'],
@@ -351,9 +329,6 @@ function writePluginProvenance(repoRoot, target) {
351
329
  mcp_launcher: { root: 'plugin', path: path.join('runtime', 'launch.cjs') },
352
330
  hook_event_helper: { root: 'plugin', path: path.join('runtime', 'hook-event.cjs') },
353
331
  hook_adapter: { root: 'plugin', path: path.join('hooks', 'adapters', 'kimi.py') },
354
- session_bootstrap: {
355
- root: 'plugin', path: path.join('skills', BOOTSTRAP_SKILL, 'SKILL.md'),
356
- },
357
332
  },
358
333
  });
359
334
  return { file, manifest };
@@ -444,7 +419,7 @@ function doctor(ctx = {}) {
444
419
  && manifest.skills.includes('./skills')
445
420
  && Array.isArray(manifest.commands)
446
421
  && manifest.commands.includes('./commands')
447
- && manifest.sessionStart?.skill === BOOTSTRAP_SKILL;
422
+ && manifest.sessionStart === undefined;
448
423
  const mcp = manifest.mcpServers?.[MCP_SERVER_NAME];
449
424
  const expected = mcpManifest();
450
425
  mcpOk = !!mcp
@@ -508,7 +483,6 @@ module.exports = {
508
483
  name: 'kimi',
509
484
  PLUGIN_ID,
510
485
  MCP_SERVER_NAME,
511
- BOOTSTRAP_SKILL,
512
486
  buildHooksManifest,
513
487
  kimiTextTransform,
514
488
  resolveTarget,
@@ -38,6 +38,11 @@ const PLUGIN_MARKER = '// Managed by Ultra Builder Pro.';
38
38
  const PROVENANCE_FILE = 'provenance.json';
39
39
  const COMMAND_NAMES = CORE_PUBLIC_SKILLS;
40
40
 
41
+ function discoverableSkillNames() {
42
+ return skillsForRuntime('opencode')
43
+ .filter((name) => !CORE_PUBLIC_SKILLS.includes(name));
44
+ }
45
+
41
46
  function resolveTarget(ctx = {}) {
42
47
  if (ctx.configDir) return ctx.configDir;
43
48
  if (ctx.scope === 'global') {
@@ -97,16 +102,26 @@ function openCodeTextTransform(input, assetName = '') {
97
102
  return text;
98
103
  }
99
104
 
100
- function openCodeCommandTransform(buf, relPath, commandName) {
105
+ function openCodeCommandTransform(buf, relPath, commandName, workflowFile) {
101
106
  if (!relPath.endsWith('.md')) return buf;
102
- const { fm, body } = parseFm(buf.toString('utf8'));
103
- if (!fm) return Buffer.from(openCodeTextTransform(buf.toString('utf8'), commandName), 'utf8');
107
+ const { fm } = parseFm(buf.toString('utf8'));
108
+ if (!fm) throw new Error(`missing frontmatter in OpenCode command: ${commandName}`);
104
109
  const nativeFm = {
105
110
  description: openCodeTextTransform(fm.description || `${commandName} workflow`, commandName)
106
111
  .replace(/\s+/g, ' ')
107
112
  .trim(),
108
113
  };
109
- return Buffer.from(serializeFm(nativeFm, openCodeTextTransform(body, commandName)), 'utf8');
114
+ const body = [
115
+ `# Run ${commandName}`,
116
+ '',
117
+ `Read and follow \`${workflowFile}\` as the only workflow definition.`,
118
+ 'This explicit command is the activation boundary; do not substitute a model-discovered',
119
+ 'public skill or start another public Ultra workflow automatically.',
120
+ '',
121
+ 'Arguments: $ARGUMENTS',
122
+ '',
123
+ ].join('\n');
124
+ return Buffer.from(serializeFm(nativeFm, body), 'utf8');
110
125
  }
111
126
 
112
127
  function openCodeSkillAssetTransform(buf, relPath, skillName) {
@@ -229,7 +244,10 @@ function copyCommands(repoRoot, target) {
229
244
  const file = `${name}.md`;
230
245
  const source = path.join(repoRoot, 'commands', file);
231
246
  if (!fs.existsSync(source)) throw new Error(`missing allowlisted OpenCode command: ${file}`);
232
- const transformed = withManagedMarker(openCodeCommandTransform(fs.readFileSync(source), file, name));
247
+ const workflowFile = path.join(target, BUNDLE_DIR, 'workflows', name, 'SKILL.md');
248
+ const transformed = withManagedMarker(
249
+ openCodeCommandTransform(fs.readFileSync(source), file, name, workflowFile),
250
+ );
233
251
  writeAtomic(path.join(output, file), transformed);
234
252
  copied.push(file);
235
253
  }
@@ -238,7 +256,7 @@ function copyCommands(repoRoot, target) {
238
256
 
239
257
  function copySkills(repoRoot, target) {
240
258
  const copied = [];
241
- const names = skillsForRuntime('opencode');
259
+ const names = discoverableSkillNames();
242
260
  const skillsRoot = path.join(target, 'skills');
243
261
  ensureDir(skillsRoot);
244
262
  for (const entry of fs.readdirSync(skillsRoot, { withFileTypes: true })) {
@@ -262,6 +280,22 @@ function copySkills(repoRoot, target) {
262
280
  return copied;
263
281
  }
264
282
 
283
+ function copyPrivateWorkflows(repoRoot, bundleRoot) {
284
+ const copied = [];
285
+ const workflowsRoot = path.join(bundleRoot, 'workflows');
286
+ for (const name of CORE_PUBLIC_SKILLS) {
287
+ const source = path.join(repoRoot, 'skills', name);
288
+ if (!fs.existsSync(path.join(source, 'SKILL.md'))) {
289
+ throw new Error(`missing allowlisted OpenCode workflow: ${name}`);
290
+ }
291
+ const files = copyTree(source, path.join(workflowsRoot, name), {
292
+ transform: (buf, relPath) => openCodeSkillAssetTransform(buf, relPath, name),
293
+ });
294
+ copied.push(...files.map((rel) => path.join('workflows', name, rel)));
295
+ }
296
+ return copied;
297
+ }
298
+
265
299
  function copyAgents(repoRoot, target) {
266
300
  const sourceRoot = path.join(repoRoot, 'agents');
267
301
  const output = path.join(target, 'agents');
@@ -279,7 +313,7 @@ function preflightAssets(repoRoot, target) {
279
313
  for (const name of COMMAND_NAMES) {
280
314
  assertManagedTextTarget(path.join(target, 'commands', `${name}.md`), 'command');
281
315
  }
282
- for (const name of skillsForRuntime('opencode')) {
316
+ for (const name of discoverableSkillNames()) {
283
317
  const destination = path.join(target, 'skills', name);
284
318
  if (fs.existsSync(destination) && !isManaged(destination)) {
285
319
  throw new Error(`refusing to replace unmanaged OpenCode skill: ${destination}`);
@@ -316,8 +350,8 @@ const BREADCRUMB_CLI = path.resolve(
316
350
  function findUltraRoot(directory) {
317
351
  let current = path.resolve(directory);
318
352
  while (true) {
319
- const ultra = path.join(current, ".ultra");
320
- if (fs.existsSync(ultra) && fs.statSync(ultra).isDirectory()) return current;
353
+ const stateDb = path.join(current, ".ultra", "state.db");
354
+ if (fs.existsSync(stateDb) && fs.statSync(stateDb).isFile()) return current;
321
355
  const parent = path.dirname(current);
322
356
  if (parent === current) return null;
323
357
  current = parent;
@@ -335,12 +369,13 @@ function readUltraContext(directory) {
335
369
  timeout: 5000,
336
370
  });
337
371
  const value = JSON.parse(raw);
338
- if (!value?.breadcrumb || typeof value.text !== "string" || !value.text) {
339
- throw new Error("canonical breadcrumb response is incomplete");
372
+ // A canonical breadcrumb is injectable only while DB authority proves an active workflow.
373
+ if (!value?.breadcrumb || !value.breadcrumb.workflow
374
+ || typeof value.text !== "string" || !value.text) {
375
+ return { root, breadcrumb: null, text: null };
340
376
  }
341
377
  return { root, breadcrumb: value.breadcrumb, text: value.text };
342
- } catch (error) {
343
- process.stderr.write(\`[ultra-builder-pro] cannot read canonical breadcrumb: \${error.message}\\n\`);
378
+ } catch {
344
379
  return { root, breadcrumb: null, text: null };
345
380
  }
346
381
  }
@@ -415,6 +450,7 @@ function install(ctx = {}) {
415
450
  const bundleRoot = path.join(target, BUNDLE_DIR);
416
451
  if (fs.existsSync(bundleRoot)) removeTree(bundleRoot);
417
452
  ensureDir(bundleRoot);
453
+ report.copied.workflows = copyPrivateWorkflows(repoRoot, bundleRoot);
418
454
  const runtime = buildMcpRuntime(repoRoot, bundleRoot, { runtime: 'opencode' });
419
455
  markManaged(bundleRoot, { adapter: 'opencode', asset: 'runtime-bundle' });
420
456
  const configFile = path.join(target, 'opencode.json');
@@ -433,7 +469,7 @@ function install(ctx = {}) {
433
469
  const bundleAssets = provenance.assetRefsForTree('config', bundleRoot, {
434
470
  exclude: [PROVENANCE_FILE],
435
471
  }).map((asset) => ({ ...asset, path: path.join(BUNDLE_DIR, asset.path) }));
436
- const skillMarkers = skillsForRuntime('opencode').map((name) => ({
472
+ const skillMarkers = discoverableSkillNames().map((name) => ({
437
473
  root: 'config', path: path.join('skills', name, '.ubp-managed'),
438
474
  }));
439
475
  const provenanceFile = path.join(bundleRoot, PROVENANCE_FILE);
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Build evidence with model-selected semantic coverage and adaptive user alignment
2
+ description: Build evidence with recommended, owner-selected semantic coverage and adaptive alignment
3
3
  argument-hint: "[scope-or-question]"
4
4
  workflow-ref: "@skills/ultra-research/SKILL.md"
5
5
  ---
@@ -12,8 +12,8 @@ Ultra Builder Pro owns only:
12
12
  `ultra-think`, plus the daily `ultra-change` and diagnostic `ultra-doctor`;
13
13
  - four internal agent-only rule skills: `code-review-expert`, `security-rules`,
14
14
  `integration-rules`, and `testing-rules`;
15
- - the host-specific collaboration companions, `ultra-verify`, and the minimal
16
- Kimi session bootstrap required to establish the shared runtime boundary;
15
+ - the host-specific collaboration companions, `ultra-verify`, and host-native
16
+ explicit command or skill entry points;
17
17
  - bounded review/debug agents, workflow-only hooks, MCP task state, and the
18
18
  portable CLI/orchestrator.
19
19
 
@@ -44,7 +44,7 @@ reports are projections or workflow artifacts.
44
44
 
45
45
  ## 3. Live MCP and declared contracts
46
46
 
47
- `spec/mcp-tools.yaml` declares and the bundled server registers 50 tools across
47
+ `spec/mcp-tools.yaml` declares and the bundled server registers 51 tools across
48
48
  eight families:
49
49
 
50
50
  | Family | Live tools |
@@ -53,15 +53,16 @@ eight families:
53
53
  | `task.*` | create, update, list, get, switch_tag, delete, init_project, expand, parse_prd, dependency_topo, append_event, subscribe_events |
54
54
  | `session.*` | spawn, close, get, list, admission_check, heartbeat, subscribe_events |
55
55
  | `change.*` | create, update, get, list, context, breadcrumb, learning_propose, learning_resolve, converge, archive |
56
- | `decision.*` | thread_start, get, list, open, resolve, delegate, defer, supersede, checkpoint |
56
+ | `decision.*` | thread_start, get, list, open, resolve, delegate, defer, supersede, complete, checkpoint |
57
57
  | `workflow.*` | start, get, list, step, complete |
58
58
  | `system.*` | doctor |
59
59
  | `plan.*` | export, get |
60
60
 
61
61
  Review, repository impact discovery, skill loading, and decision presentation remain
62
62
  host-native capabilities rather than fake MCP contracts. MCP stores only normalized
63
- decision authority and checkpoints; it never generates questions or retains prompts
64
- and transcripts. The generated Codex capability map documents host replacements.
63
+ decision authority, pending-question recovery state, lifecycle completion, and optional
64
+ checkpoints; it never generates questions or retains prompts and transcripts. The
65
+ generated Codex capability map documents host replacements.
65
66
 
66
67
  The complete write, transition, invalidation, and recovery contract lives in
67
68
  [`WORKFLOW-LIFECYCLE.md`](./WORKFLOW-LIFECYCLE.md).
@@ -79,11 +80,13 @@ review, and deliver route back to the same thread instead of deciding for the ow
79
80
 
80
81
  One partial unique index permits only one open item per thread. `decision.resolve`,
81
82
  `decision.delegate`, and `decision.defer` preserve the source of authority;
82
- `decision.supersede` preserves history when evidence or intent changes. Prepare and
83
- confirm checkpointing bind a material accepted cluster to current artifact digests
84
- when interruption recovery needs that boundary. Status and breadcrumb return only the
85
- current question plus allowed and mechanically required transitions, so recovery does
86
- not require replaying conversation history.
83
+ `decision.supersede` preserves history when evidence or intent changes.
84
+ `decision.complete` closes settled normalized state without manufacturing an approval
85
+ receipt. Prepare and confirm checkpointing remain optional and bind only a material
86
+ accepted cluster to current artifact digests when interruption recovery needs that
87
+ boundary. Status and breadcrumb return only the current question plus allowed and
88
+ mechanically required transitions, so recovery does not require replaying conversation
89
+ history.
87
90
 
88
91
  ## 5. Context Spine contract
89
92
 
@@ -191,31 +194,17 @@ Archive before prune; the confirmation token is intentionally required.
191
194
  └── delivery/<workflow-id>.json
192
195
  ```
193
196
 
194
- ## 10. User handbook integration
197
+ ## 10. Plugin and user-instruction isolation
195
198
 
196
- General engineering doctrine lives in the host's durable `CLAUDE.md` or `AGENTS.md`.
197
- Ultra Builder Pro supports two explicit operations. Default mode updates only the
198
- bounded runtime contract:
199
+ General engineering doctrine and long-term personal preferences live in each
200
+ host's durable `CLAUDE.md` or `AGENTS.md`. Ultra never writes those files. Its
201
+ workflow doctrine remains in plugin-owned skills and is loaded only through an
202
+ explicit public invocation.
199
203
 
200
- ```bash
201
- ubp-handbook preview --runtime codex
202
- ubp-handbook apply --runtime codex
203
- ```
204
-
205
- Full mode replaces legacy unmarked prompt content with one complete host-native
206
- engineering handbook:
207
-
208
- ```bash
209
- ubp-handbook preview --runtime codex --full > /tmp/ubp-codex-handbook.md
210
- ubp-handbook apply --runtime codex --full \
211
- --confirm "PASTE_64_CHARACTER_TOKEN_HERE"
212
- ```
213
-
214
- Supported runtime names are `claude`, `codex`, `opencode`, and `kimi`. The full
215
- preview prints its content-bound confirmation token to stderr. Both apply modes
216
- create a timestamped backup. Full mode preserves supported marker-delimited blocks
217
- owned by other providers, follows handbook symlinks, and rejects missing or stale
218
- confirmation. Plugin adapters never overwrite handbooks implicitly.
204
+ Installation and uninstall own only adapter-declared commands, skills, workers,
205
+ hooks, runtime assets, provenance, and host registration. Project authority is
206
+ created only by an explicit `ultra-init`, and uninstall never deletes `.ultra/`.
207
+ See [`PLUGIN-ISOLATION-CONTRACT.md`](./PLUGIN-ISOLATION-CONTRACT.md).
219
208
 
220
209
  ## 11. Verification
221
210