ultra-builder-pro-cli 0.9.0 → 0.10.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.
- package/CHANGELOG.md +47 -0
- package/README.md +11 -6
- package/adapters/_shared/codex-assets.cjs +10 -0
- package/adapters/_shared/handbook.cjs +2 -0
- package/adapters/_shared/runtime-assets.cjs +1 -0
- package/adapters/_shared/tests/handbook.test.cjs +3 -0
- package/adapters/_shared/tests/runtime-assets.test.cjs +1 -0
- package/adapters/kimi.js +2 -2
- package/adapters/opencode.js +46 -17
- package/adapters/tests/claude.test.cjs +4 -1
- package/adapters/tests/codex.test.cjs +4 -3
- package/adapters/tests/kimi.test.cjs +6 -2
- package/adapters/tests/opencode.test.cjs +46 -3
- package/docs/AGENT-CONTEXT.md +34 -12
- package/docs/ARCHITECTURE.md +31 -5
- package/docs/PLAN.zh-CN.md +3 -2
- package/docs/ROADMAP.md +5 -4
- package/docs/RUNTIME-COMPAT-MATRIX.md +7 -5
- package/docs/USER-HANDBOOK-CONTRACT.md +9 -2
- package/hooks/README.md +6 -4
- package/hooks/active_task_context.py +14 -0
- package/hooks/context_spine.py +218 -0
- package/hooks/health_check.py +1 -1
- package/hooks/workflow_context.py +16 -123
- package/hooks/workflow_resume.py +21 -0
- package/mcp-server/lib/change-workflow.cjs +89 -11
- package/mcp-server/lib/change-workflow.test.cjs +202 -7
- package/mcp-server/lib/context-spine.cjs +331 -0
- package/mcp-server/lib/context-spine.test.cjs +73 -0
- package/mcp-server/lib/spec-learning.cjs +191 -0
- package/mcp-server/lib/spec-learning.test.cjs +62 -0
- package/mcp-server/lib/state-db.cjs +72 -10
- package/mcp-server/lib/state-db.test.cjs +7 -2
- package/mcp-server/server.cjs +31 -1
- package/mcp-server/tests/db-ops.test.cjs +3 -3
- package/mcp-server/tests/server-contract.test.cjs +74 -0
- package/mcp-server/tests/state-db.test.cjs +20 -3
- package/package.json +1 -1
- package/skills/learn/SKILL.md +1 -0
- package/skills/ultra-change/SKILL.md +91 -118
- package/skills/ultra-deliver/SKILL.md +93 -221
- package/skills/ultra-dev/SKILL.md +81 -310
- package/skills/ultra-doctor/SKILL.md +1 -0
- package/skills/ultra-init/SKILL.md +1 -0
- package/skills/ultra-plan/SKILL.md +72 -292
- package/skills/ultra-review/SKILL.md +79 -407
- package/skills/ultra-status/SKILL.md +55 -168
- package/skills/ultra-test/SKILL.md +64 -183
- package/skills/ultra-think/SKILL.md +1 -0
- package/spec/cli-protocol.md +3 -0
- package/spec/mcp-tools.yaml +133 -4
- package/spec/schemas/state-db.sql +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.10.0] — 2026-07-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added Context Manifest v2 and a DB-derived Context Spine across planning,
|
|
15
|
+
implementation, checking, review, convergence, and recovery. Context snapshots
|
|
16
|
+
now carry role, gate, readiness blockers, bounded references and digests,
|
|
17
|
+
fresh-context budgets, a public seam, an exact verification command, and one
|
|
18
|
+
deterministic next action.
|
|
19
|
+
- Added `change.breadcrumb`, `change.learning_propose`, and
|
|
20
|
+
`change.learning_resolve`, advancing the live MCP contract to 32 tools and
|
|
21
|
+
`.ultra/state.db` to schema 10.0 with 16 tables.
|
|
22
|
+
- Added approval-gated specification-learning candidates. Proposed or approved
|
|
23
|
+
discoveries block convergence until rejected or applied to a declared baseline
|
|
24
|
+
target, and `change.get` exposes their authoritative state.
|
|
25
|
+
- Added independent `spec_fidelity` and `engineering_standards` review evidence,
|
|
26
|
+
plus public-seam and exact red/green signal fields for convergence.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Rebuilt the daily harness around one status router, one highest-value alignment
|
|
31
|
+
question, fresh-context tracer bullets, explicit expand contracts, independent
|
|
32
|
+
check/review contexts, and convergence before release.
|
|
33
|
+
- Reduced the seven core execution workflow prompts from 1,922 to 758 lines and
|
|
34
|
+
added a 220-line ceiling for every public Ultra skill so host prompts cannot
|
|
35
|
+
silently accumulate duplicated procedure. All twelve public workflows are now
|
|
36
|
+
explicitly user-invocable; four rule skills remain agent-only.
|
|
37
|
+
- Session-start, pre-edit, and recovery hooks now share one read-only
|
|
38
|
+
`context_spine.py` helper and inject only the compact DB breadcrumb. Intent,
|
|
39
|
+
transcript, memory-provider, and graph-provider payloads never enter hook context.
|
|
40
|
+
- OpenCode now consumes the DB-generated Context Manifest v2 projection, strips
|
|
41
|
+
provider/intent content, and fails context readiness when its compiled HEAD
|
|
42
|
+
differs from the current repository.
|
|
43
|
+
- Updated Claude Code, Codex, OpenCode, and Kimi Code native renderings, managed
|
|
44
|
+
handbook blocks, MCP mapping, architecture, and compatibility documentation.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- Prevented plan-role task context from becoming ready without an execution
|
|
49
|
+
contract, public seam, and verification command.
|
|
50
|
+
- Prevented breadcrumbs from reusing a snapshot after task state or git HEAD
|
|
51
|
+
changed, and made an explicitly missing change id fail closed.
|
|
52
|
+
- Added auditable 9.1 → 10.0 migration history while preserving existing runtime
|
|
53
|
+
rows, references, projections, and foreign-key integrity.
|
|
54
|
+
- Corrected hook conformance to package `context_spine.py` as an imported helper
|
|
55
|
+
without registering it as a duplicate lifecycle hook.
|
|
56
|
+
|
|
10
57
|
## [0.9.0] — 2026-07-17
|
|
11
58
|
|
|
12
59
|
### Added
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ authoritative `.ultra/state.db`.
|
|
|
8
8
|
|
|
9
9
|
<div align="center">
|
|
10
10
|
|
|
11
|
-
[](./CHANGELOG.md)
|
|
12
12
|
[](#verification)
|
|
13
13
|
[](./LICENSE)
|
|
14
14
|
[](./package.json)
|
|
@@ -36,8 +36,10 @@ authoritative `.ultra/state.db`.
|
|
|
36
36
|
- **Runs real PRDs end-to-end.** `task.parse_prd` → `lib/topo.cjs` waves →
|
|
37
37
|
`.ultra/execution-plan.json` → parallel worktree sessions → auto-merge back.
|
|
38
38
|
- **Keeps daily work convergent.** `/ultra-change` creates a bounded delta and
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
Context Manifest v2; the Context Spine assigns a role/gate, verifies required
|
|
40
|
+
references and fresh-context budget, exposes one DB-derived breadcrumb, and
|
|
41
|
+
routes a single next action. `/ultra-deliver` requires applied specification
|
|
42
|
+
learning plus docs/test/two-axis review evidence before archive. `/ultra-doctor` reports incidents,
|
|
41
43
|
projection lag, orphan sessions, and backup-first mechanical recovery.
|
|
42
44
|
Incident changes additionally require a five-section `diagnosis.md` covering
|
|
43
45
|
reproduction, hypotheses, root cause, regression test, and recovery.
|
|
@@ -91,7 +93,7 @@ for per-runtime capabilities.
|
|
|
91
93
|
The layers share one `.ultra/state.db`, but the CLI is not a mirror of every MCP
|
|
92
94
|
tool: continuous change mutations are MCP-only and fail closed. See
|
|
93
95
|
[`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) for the full contract and
|
|
94
|
-
[`spec/cli-protocol.md`](./spec/cli-protocol.md) for the
|
|
96
|
+
[`spec/cli-protocol.md`](./spec/cli-protocol.md) for the 32 live contracts.
|
|
95
97
|
Review, impact discovery, skill resolution, and user interaction stay on each
|
|
96
98
|
Host's native surfaces; the generated Codex capability map documents those
|
|
97
99
|
replacements without advertising non-existent MCP tools.
|
|
@@ -137,8 +139,11 @@ ultra-tools system doctor
|
|
|
137
139
|
|
|
138
140
|
Or let the skills drive it. In Codex, invoke
|
|
139
141
|
`$ultra-builder-pro:ultra-plan` → `$ultra-builder-pro:ultra-dev` →
|
|
140
|
-
`$ultra-builder-pro:ultra-
|
|
141
|
-
|
|
142
|
+
`$ultra-builder-pro:ultra-test` → `$ultra-builder-pro:ultra-review` →
|
|
143
|
+
`$ultra-builder-pro:ultra-deliver`; after the baseline is delivered, daily work
|
|
144
|
+
starts with `$ultra-builder-pro:ultra-change`, while
|
|
145
|
+
`$ultra-builder-pro:ultra-status` reads the compact authoritative breadcrumb.
|
|
146
|
+
Kimi uses the corresponding
|
|
142
147
|
`/ultra-builder-pro:ultra-*` namespace. Other runtimes retain their native
|
|
143
148
|
command form.
|
|
144
149
|
|
|
@@ -467,6 +467,16 @@ and does not require an interactive MCP or legacy CLI menu.
|
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
if (skillName === 'ultra-review') {
|
|
470
|
+
text = text.replace(
|
|
471
|
+
'# $ultra-builder-pro:ultra-review - Ultra Review System',
|
|
472
|
+
`# $ultra-builder-pro:ultra-review - Ultra Review System
|
|
473
|
+
|
|
474
|
+
## Codex native worker contract
|
|
475
|
+
|
|
476
|
+
Use the installed native Codex custom agents as bounded review workers. Dispatch independent
|
|
477
|
+
workers concurrently, keep the current Codex task primary, and treat their JSON artifacts—not
|
|
478
|
+
their conversational transcripts—as review evidence.`,
|
|
479
|
+
);
|
|
470
480
|
text = text.replaceAll('### Phase 3: Background Execution', '### Phase 3: Parallel Native Agent Execution');
|
|
471
481
|
text = text.replaceAll('**Step 4b: Launch coordinator in background:**', '**Step 4b: Delegate coordination:**');
|
|
472
482
|
text = text.replaceAll('in **background mode** (`native parallel execution`)', 'concurrently with native Codex subagent orchestration');
|
|
@@ -60,6 +60,8 @@ function renderHandbook(runtime) {
|
|
|
60
60
|
`- Collaboration boundary: ${value.collaboration}. The current host remains primary and owns final verification.`,
|
|
61
61
|
`- Coordination boundary: ${value.coordination}.`,
|
|
62
62
|
'- Authority: `.ultra/state.db` is the only durable Ultra authority for changes, tasks, sessions, events, incidents, projection state, telemetry, and review evidence. Generated JSON and Markdown are projections or workflow artifacts.',
|
|
63
|
+
'- Context Spine boundary: `change.context` compiles role/gate readiness, bounded references, fresh-context budget, public seam, verification command, and one next action. Hooks inject only the DB-derived `change.breadcrumb`; stale or blocked context must be recompiled.',
|
|
64
|
+
'- Specification learning boundary: stable implementation discoveries use approval-gated propose/approve-or-reject/apply transitions. Unresolved learning blocks convergence; review requires independent specification-fidelity and engineering-standards evidence.',
|
|
63
65
|
'- Memory and graph boundary: Ultra Builder Pro does not capture prompts, transcripts, observations, summaries, cross-session memory, or code-graph content. Separately installed providers own that data; Ultra may store only their metadata references in a change context manifest.',
|
|
64
66
|
'- Hook boundary: Ultra hooks observe workflow/change lifecycle only. Health/context may run when `.ultra/state.db` exists; projection protection is always authoritative, while edit/compact/stop/subagent enforcement remains active-workflow scoped. Generic command blocking and post-edit policy stay in user or repository governance.',
|
|
65
67
|
'- Installation boundary: `ubp --doctor` is the read-only authority for installed asset provenance, content hashes, and host entry-point wiring; `system.doctor` remains scoped to project state and workflow recovery.',
|
|
@@ -33,6 +33,9 @@ test('renders one common contract with host-native invocation syntax', () => {
|
|
|
33
33
|
assert.match(rendered, /ultra-change/);
|
|
34
34
|
assert.match(rendered, /ultra-doctor/);
|
|
35
35
|
assert.match(rendered, /metadata references/);
|
|
36
|
+
assert.match(rendered, /Context Spine boundary/);
|
|
37
|
+
assert.match(rendered, /change\.breadcrumb/);
|
|
38
|
+
assert.match(rendered, /Specification learning boundary/);
|
|
36
39
|
assert.doesNotMatch(rendered, /\.ultra\/memory|memory\.retain|memory\.recall/);
|
|
37
40
|
assert.match(rendered, new RegExp(BEGIN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
|
38
41
|
assert.match(rendered, new RegExp(END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
|
@@ -83,6 +83,7 @@ test('code-review-expert and rule skills are internal, never implicitly exposed'
|
|
|
83
83
|
test('workflow hook allowlist contains no memory, prompt capture, or generic policy hook', () => {
|
|
84
84
|
assert.deepEqual(WORKFLOW_HOOK_FILES, [
|
|
85
85
|
'active_task_context.py',
|
|
86
|
+
'context_spine.py',
|
|
86
87
|
'health_check.py',
|
|
87
88
|
'pre_stop_check.py',
|
|
88
89
|
'subagent_tracker.py',
|
package/adapters/kimi.js
CHANGED
|
@@ -219,8 +219,8 @@ bundled files are bounded prompt templates, not separately registered host agent
|
|
|
219
219
|
'Read `SUMMARY.json` only after this validation succeeds.',
|
|
220
220
|
].join('\n');
|
|
221
221
|
text = text.replace(
|
|
222
|
-
|
|
223
|
-
`${executionContract}\n\n
|
|
222
|
+
/#{2,3} Phase 3:[\s\S]*?#{2,3} Phase 5: Report to User/,
|
|
223
|
+
`${executionContract}\n\n## Phase 5: Report to User`,
|
|
224
224
|
);
|
|
225
225
|
text = text.replaceAll('Background Execution', 'Reviewer Execution');
|
|
226
226
|
text = text.replaceAll('Wait & Coordinate', 'Validate & Coordinate');
|
package/adapters/opencode.js
CHANGED
|
@@ -336,25 +336,26 @@ function pluginSource() {
|
|
|
336
336
|
return `// Managed by Ultra Builder Pro. Rebuild through the adapter; do not edit in place.
|
|
337
337
|
import fs from "node:fs";
|
|
338
338
|
import path from "node:path";
|
|
339
|
+
import { execFileSync } from "node:child_process";
|
|
339
340
|
|
|
340
341
|
const TERMINAL = new Set(["committed", "completed", "done", "cancelled"]);
|
|
341
342
|
const TASKS_PROJECTION = ".ultra/tasks/tasks.json";
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
343
|
+
|
|
344
|
+
function gitHead(root) {
|
|
345
|
+
try {
|
|
346
|
+
return execFileSync("git", ["rev-parse", "HEAD"], {
|
|
347
|
+
cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"],
|
|
348
|
+
}).trim();
|
|
349
|
+
} catch {
|
|
350
|
+
return null;
|
|
350
351
|
}
|
|
351
|
-
return clean;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
function findUltraContext(directory) {
|
|
355
355
|
let current = path.resolve(directory);
|
|
356
356
|
while (true) {
|
|
357
357
|
const ultra = path.join(current, ".ultra");
|
|
358
|
+
const currentHead = gitHead(current);
|
|
358
359
|
const workflowFile = path.join(ultra, "workflow-state.json");
|
|
359
360
|
const statePresent = fs.existsSync(path.join(ultra, "state.db"));
|
|
360
361
|
let workflow = null;
|
|
@@ -375,11 +376,36 @@ function findUltraContext(directory) {
|
|
|
375
376
|
if (!fs.existsSync(manifestPath)) continue;
|
|
376
377
|
try {
|
|
377
378
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
378
|
-
if (manifest?.change)
|
|
379
|
-
|
|
380
|
-
|
|
379
|
+
if (manifest?.change) {
|
|
380
|
+
const compiledHead = manifest.git?.head ?? null;
|
|
381
|
+
const legacyContext = manifest.schema_version !== "2.0";
|
|
382
|
+
const headStale = Boolean(compiledHead && currentHead && compiledHead !== currentHead);
|
|
383
|
+
const blockers = Array.isArray(manifest.readiness?.blockers)
|
|
384
|
+
? [...manifest.readiness.blockers]
|
|
385
|
+
: [];
|
|
386
|
+
if (headStale && !blockers.includes("CONTEXT_HEAD_STALE")) blockers.push("CONTEXT_HEAD_STALE");
|
|
387
|
+
if (legacyContext && !blockers.includes("CONTEXT_SNAPSHOT_UPGRADE_REQUIRED")) {
|
|
388
|
+
blockers.unshift("CONTEXT_SNAPSHOT_UPGRADE_REQUIRED");
|
|
389
|
+
}
|
|
390
|
+
changes.push({
|
|
391
|
+
id: manifest.change.id,
|
|
392
|
+
kind: manifest.change.kind,
|
|
393
|
+
status: manifest.change.status,
|
|
394
|
+
role: manifest.role ?? "plan",
|
|
395
|
+
gate: manifest.gate ?? "alignment",
|
|
396
|
+
nextAction: legacyContext
|
|
397
|
+
? "Recompile change.context to upgrade this legacy context snapshot."
|
|
398
|
+
: headStale
|
|
399
|
+
? "Git HEAD changed after context compilation; recompile change.context."
|
|
400
|
+
: (manifest.next_action ?? "Inspect Ultra status."),
|
|
401
|
+
readiness: legacyContext || headStale ? "blocked" : (manifest.readiness?.status ?? "ready"),
|
|
402
|
+
blockers,
|
|
403
|
+
taskId: manifest.selected_task?.id ?? manifest.resume?.task_id ?? null,
|
|
404
|
+
taskStatus: manifest.selected_task?.status ?? manifest.resume?.task_status ?? null,
|
|
405
|
+
tokenEstimate: manifest.context?.token_estimate ?? 0,
|
|
381
406
|
manifestPath: path.relative(current, manifestPath),
|
|
382
407
|
});
|
|
408
|
+
}
|
|
383
409
|
} catch (error) {
|
|
384
410
|
process.stderr.write(\`[ultra-builder-pro] cannot read \${manifestPath}: \${error.message}\\n\`);
|
|
385
411
|
}
|
|
@@ -410,13 +436,16 @@ function contextText(active) {
|
|
|
410
436
|
if (active.changes.length > 0) {
|
|
411
437
|
const change = active.changes[0];
|
|
412
438
|
lines.push(
|
|
413
|
-
"[Ultra
|
|
439
|
+
"[Ultra context spine]",
|
|
414
440
|
\`Project: \${active.root}\`,
|
|
415
441
|
\`Change: \${change.id} (\${change.kind ?? "unknown"}, \${change.status ?? "active"})\`,
|
|
416
|
-
\`
|
|
417
|
-
\`
|
|
442
|
+
\`Task: \${change.taskId ?? "none"} (\${change.taskStatus ?? "none"})\`,
|
|
443
|
+
\`Role: \${change.role}\`,
|
|
444
|
+
\`Gate: \${change.gate}\`,
|
|
445
|
+
\`Readiness: \${change.readiness}\`,
|
|
446
|
+
...(change.blockers.length > 0 ? [\`Blockers: \${change.blockers.join(", ")}\`] : []),
|
|
447
|
+
\`Next: \${change.nextAction}\`,
|
|
418
448
|
\`Context manifest: \${change.manifestPath}\`,
|
|
419
|
-
\`External providers (metadata references only): \${JSON.stringify(change.providers)}\`,
|
|
420
449
|
);
|
|
421
450
|
if (active.changes.length > 1) lines.push(
|
|
422
451
|
\`Active change count: \${active.changes.length}; resolve scope with change.list before mutation.\`,
|
|
@@ -429,7 +458,7 @@ function contextText(active) {
|
|
|
429
458
|
"No active continuous change. Start daily work with the ultra-change workflow.",
|
|
430
459
|
);
|
|
431
460
|
}
|
|
432
|
-
lines.push("Authority: .ultra/state.db;
|
|
461
|
+
lines.push("Authority: .ultra/state.db; JSON/Markdown remain projections or evidence artifacts.");
|
|
433
462
|
return lines.join("\\n");
|
|
434
463
|
}
|
|
435
464
|
|
|
@@ -36,7 +36,10 @@ test('install builds the Claude-native plugin from the explicit Ultra allowlist'
|
|
|
36
36
|
|
|
37
37
|
const hooks = JSON.parse(fs.readFileSync(path.join(target, 'hooks', 'hooks.json'), 'utf8'));
|
|
38
38
|
const serialized = JSON.stringify(hooks);
|
|
39
|
-
for (const name of WORKFLOW_HOOK_FILES
|
|
39
|
+
for (const name of WORKFLOW_HOOK_FILES.filter((value) => value !== 'context_spine.py')) {
|
|
40
|
+
assert.match(serialized, new RegExp(name.replace('.', '\\.')));
|
|
41
|
+
}
|
|
42
|
+
assert.ok(fs.existsSync(path.join(target, 'hooks', 'context_spine.py')));
|
|
40
43
|
assert.doesNotMatch(serialized, /memory|recall|journal|observation_capture|user_prompt_capture|block_dangerous|post_edit_guard/);
|
|
41
44
|
assert.match(serialized, /\$\{CLAUDE_PLUGIN_ROOT\}/);
|
|
42
45
|
} finally {
|
|
@@ -198,9 +198,10 @@ test('plugin declares current Codex hooks and a project-local Ultra MCP server',
|
|
|
198
198
|
'SubagentStart', 'SubagentStop',
|
|
199
199
|
].sort());
|
|
200
200
|
const serializedHooks = JSON.stringify(hooks);
|
|
201
|
-
for (const feature of WORKFLOW_HOOK_FILES) {
|
|
201
|
+
for (const feature of WORKFLOW_HOOK_FILES.filter((value) => value !== 'context_spine.py')) {
|
|
202
202
|
assert.match(serializedHooks, new RegExp(feature.replace('.', '\\.')));
|
|
203
203
|
}
|
|
204
|
+
assert.ok(fs.existsSync(path.join(layout.pluginRoot, 'hooks', 'context_spine.py')));
|
|
204
205
|
assert.doesNotMatch(serializedHooks, /memory|recall|journal|observation_capture|user_prompt_capture|block_dangerous|post_edit_guard/);
|
|
205
206
|
assert.match(serializedHooks, /\$PLUGIN_ROOT\/hooks\/adapters\/codex\.py/);
|
|
206
207
|
assert.match(serializedHooks, /apply_patch/);
|
|
@@ -231,8 +232,8 @@ test('plugin declares current Codex hooks and a project-local Ultra MCP server',
|
|
|
231
232
|
const liveSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'mcp-tools.yaml'), 'utf8'));
|
|
232
233
|
const upstreamSpec = yaml.load(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'upstream-mcp-tools.yaml'), 'utf8'));
|
|
233
234
|
const capabilityMap = JSON.parse(fs.readFileSync(path.join(layout.pluginRoot, 'spec', 'codex-capability-map.json'), 'utf8'));
|
|
234
|
-
assert.equal(liveSpec.tools.length,
|
|
235
|
-
assert.equal(upstreamSpec.tools.length,
|
|
235
|
+
assert.equal(liveSpec.tools.length, 32);
|
|
236
|
+
assert.equal(upstreamSpec.tools.length, 32);
|
|
236
237
|
assert.deepEqual(upstreamSpec.tools.map((tool) => tool.name).sort(), liveSpec.tools.map((tool) => tool.name).sort());
|
|
237
238
|
assert.deepEqual(capabilityMap.live_mcp_tools.sort(), liveSpec.tools.map((tool) => tool.name).sort());
|
|
238
239
|
assert.equal(Object.keys(capabilityMap.codex_native_replacements).length, 9);
|
|
@@ -85,7 +85,10 @@ test('Kimi manifest exposes native skills, commands, hooks, session bootstrap, a
|
|
|
85
85
|
|
|
86
86
|
const hookText = JSON.stringify(manifest.hooks);
|
|
87
87
|
assert.match(hookText, /hooks\/adapters\/kimi\.py/);
|
|
88
|
-
for (const hook of WORKFLOW_HOOK_FILES
|
|
88
|
+
for (const hook of WORKFLOW_HOOK_FILES.filter((value) => value !== 'context_spine.py')) {
|
|
89
|
+
assert.match(hookText, new RegExp(hook.replace('.', '\\.')));
|
|
90
|
+
}
|
|
91
|
+
assert.ok(fs.existsSync(path.join(pluginRoot, 'hooks', 'context_spine.py')));
|
|
89
92
|
assert.doesNotMatch(hookText, /memory|recall|journal|prompt[_ -]?capture|block_dangerous|post_edit_guard/i);
|
|
90
93
|
|
|
91
94
|
const commands = fs.readdirSync(path.join(pluginRoot, 'commands')).sort();
|
|
@@ -120,7 +123,8 @@ test('Kimi assets are allowlisted and adapted to native tools and paths', () =>
|
|
|
120
123
|
);
|
|
121
124
|
assert.match(learn, /~\/.kimi-code\/skills\/learned-<pattern-slug>-unverified\/SKILL\.md/);
|
|
122
125
|
assert.match(review, /Kimi `AgentSwarm`/);
|
|
123
|
-
assert.match(review, /
|
|
126
|
+
assert.match(review, /Five engineering specialists/);
|
|
127
|
+
assert.match(review, /axes\.spec_fidelity/);
|
|
124
128
|
assert.doesNotMatch(review, /6 specialized|ALL 6/);
|
|
125
129
|
assert.match(review, /\$KIMI_PLUGIN_ROOT\/agents\/review-code\.md/);
|
|
126
130
|
assert.match(review, /`prompt_template` containing\s+`\{\{item\}\}`/);
|
|
@@ -5,6 +5,7 @@ const assert = require('node:assert/strict');
|
|
|
5
5
|
const fs = require('node:fs');
|
|
6
6
|
const os = require('node:os');
|
|
7
7
|
const path = require('node:path');
|
|
8
|
+
const { execFileSync } = require('node:child_process');
|
|
8
9
|
const { pathToFileURL } = require('node:url');
|
|
9
10
|
|
|
10
11
|
const opencode = require('../opencode.js');
|
|
@@ -172,7 +173,8 @@ test('install writes a schema-safe opencode.json and keeps ownership outside hos
|
|
|
172
173
|
assert.match(plugin, /throw new Error/);
|
|
173
174
|
assert.match(plugin, /session\.compacted/);
|
|
174
175
|
assert.doesNotMatch(plugin, /memory\.(?:retain|recall|reflect)|journal|observation|prompt[_ -]?capture/);
|
|
175
|
-
assert.match(plugin, /
|
|
176
|
+
assert.match(plugin, /Ultra context spine/);
|
|
177
|
+
assert.doesNotMatch(plugin, /providerMetadata|External providers/);
|
|
176
178
|
} finally {
|
|
177
179
|
fs.rmSync(target, { recursive: true, force: true });
|
|
178
180
|
}
|
|
@@ -189,18 +191,59 @@ test('OpenCode context covers active changes and projection writes are blocked f
|
|
|
189
191
|
|
|
190
192
|
fs.mkdirSync(path.join(project, '.ultra'), { recursive: true });
|
|
191
193
|
fs.writeFileSync(path.join(project, '.ultra', 'state.db'), 'owned-by-ultra');
|
|
194
|
+
execFileSync('git', ['init'], { cwd: project, stdio: 'ignore' });
|
|
195
|
+
fs.writeFileSync(path.join(project, 'app.txt'), 'initial\n');
|
|
196
|
+
execFileSync('git', ['add', 'app.txt'], { cwd: project, stdio: 'ignore' });
|
|
197
|
+
execFileSync('git', ['-c', 'user.name=Ultra Tests', '-c', 'user.email=ultra@example.invalid', 'commit', '-m', 'initial'], {
|
|
198
|
+
cwd: project, stdio: 'ignore',
|
|
199
|
+
});
|
|
200
|
+
const compiledHead = execFileSync('git', ['rev-parse', 'HEAD'], { cwd: project, encoding: 'utf8' }).trim();
|
|
192
201
|
const changeRoot = path.join(project, '.ultra', 'changes', 'active', 'daily-fix');
|
|
193
202
|
fs.mkdirSync(changeRoot, { recursive: true });
|
|
194
203
|
fs.writeFileSync(path.join(changeRoot, 'context-manifest.json'), JSON.stringify({
|
|
204
|
+
schema_version: '2.0',
|
|
195
205
|
change: { id: 'daily-fix', title: 'Daily fix', kind: 'quick', status: 'active', intent: 'Fix drift' },
|
|
206
|
+
role: 'implement',
|
|
207
|
+
gate: 'implementation',
|
|
208
|
+
next_action: 'Run the exact regression test.',
|
|
209
|
+
readiness: { status: 'ready', blockers: [] },
|
|
210
|
+
git: { head: compiledHead },
|
|
211
|
+
selected_task: { id: 'daily-task', status: 'in_progress' },
|
|
212
|
+
context: { token_estimate: 1200, budget: { max_tokens: 8000, max_files: 8 } },
|
|
196
213
|
providers: { memory: { provider: 'cloud-mem', status: 'available' } },
|
|
197
214
|
}));
|
|
198
215
|
|
|
199
216
|
const output = { system: [] };
|
|
200
217
|
await plugin['experimental.chat.system.transform']({}, output);
|
|
201
|
-
assert.match(output.system.join('\n'), /Ultra
|
|
218
|
+
assert.match(output.system.join('\n'), /Ultra context spine/);
|
|
202
219
|
assert.match(output.system.join('\n'), /daily-fix/);
|
|
203
|
-
assert.match(output.system.join('\n'), /
|
|
220
|
+
assert.match(output.system.join('\n'), /daily-task/);
|
|
221
|
+
assert.match(output.system.join('\n'), /Role: implement/);
|
|
222
|
+
assert.match(output.system.join('\n'), /Gate: implementation/);
|
|
223
|
+
assert.match(output.system.join('\n'), /Readiness: ready/);
|
|
224
|
+
assert.match(output.system.join('\n'), /Run the exact regression test/);
|
|
225
|
+
assert.doesNotMatch(output.system.join('\n'), /Fix drift|cloud-mem/);
|
|
226
|
+
|
|
227
|
+
const manifestFile = path.join(changeRoot, 'context-manifest.json');
|
|
228
|
+
const legacyManifest = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
|
|
229
|
+
legacyManifest.schema_version = '1.0';
|
|
230
|
+
fs.writeFileSync(manifestFile, JSON.stringify(legacyManifest));
|
|
231
|
+
const legacyOutput = { system: [] };
|
|
232
|
+
await plugin['experimental.chat.system.transform']({}, legacyOutput);
|
|
233
|
+
assert.match(legacyOutput.system.join('\n'), /CONTEXT_SNAPSHOT_UPGRADE_REQUIRED/);
|
|
234
|
+
legacyManifest.schema_version = '2.0';
|
|
235
|
+
fs.writeFileSync(manifestFile, JSON.stringify(legacyManifest));
|
|
236
|
+
|
|
237
|
+
fs.writeFileSync(path.join(project, 'app.txt'), 'changed\n');
|
|
238
|
+
execFileSync('git', ['add', 'app.txt'], { cwd: project, stdio: 'ignore' });
|
|
239
|
+
execFileSync('git', ['-c', 'user.name=Ultra Tests', '-c', 'user.email=ultra@example.invalid', 'commit', '-m', 'change'], {
|
|
240
|
+
cwd: project, stdio: 'ignore',
|
|
241
|
+
});
|
|
242
|
+
const staleOutput = { system: [] };
|
|
243
|
+
await plugin['experimental.chat.system.transform']({}, staleOutput);
|
|
244
|
+
assert.match(staleOutput.system.join('\n'), /Readiness: blocked/);
|
|
245
|
+
assert.match(staleOutput.system.join('\n'), /CONTEXT_HEAD_STALE/);
|
|
246
|
+
assert.match(staleOutput.system.join('\n'), /recompile change\.context/);
|
|
204
247
|
|
|
205
248
|
await assert.rejects(
|
|
206
249
|
plugin['tool.execute.before'](
|
package/docs/AGENT-CONTEXT.md
CHANGED
|
@@ -44,14 +44,14 @@ 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
|
|
47
|
+
`spec/mcp-tools.yaml` declares and the bundled server registers 32 tools across
|
|
48
48
|
five families:
|
|
49
49
|
|
|
50
50
|
| Family | Live tools |
|
|
51
51
|
|---|---|
|
|
52
52
|
| `task.*` | create, update, list, get, switch_tag, delete, init_project, expand, parse_prd, dependency_topo, append_event, subscribe_events |
|
|
53
53
|
| `session.*` | spawn, close, get, list, admission_check, heartbeat, subscribe_events |
|
|
54
|
-
| `change.*` | create, update, get, list, context, converge, archive |
|
|
54
|
+
| `change.*` | create, update, get, list, context, breadcrumb, learning_propose, learning_resolve, converge, archive |
|
|
55
55
|
| `system.*` | doctor |
|
|
56
56
|
| `plan.*` | export, get |
|
|
57
57
|
|
|
@@ -62,7 +62,26 @@ capability map documents those replacements.
|
|
|
62
62
|
Any new MCP contract starts in `spec/mcp-tools.yaml` with valid and invalid
|
|
63
63
|
fixtures. Do not add an ad-hoc server handler first.
|
|
64
64
|
|
|
65
|
-
## 4.
|
|
65
|
+
## 4. Context Spine contract
|
|
66
|
+
|
|
67
|
+
Context Manifest v2 is a DB-backed role handoff, not a static codebase summary.
|
|
68
|
+
`change.context` compiles required references, digests, readiness, context budget,
|
|
69
|
+
public seam, exact verification command, and one next action for `plan`,
|
|
70
|
+
`implement`, `check`, or `review`. The default fresh-context contract is at most
|
|
71
|
+
12 files, about 12k tokens, and 40% of the host context; wider work must use an
|
|
72
|
+
explicit `expand_contract`.
|
|
73
|
+
|
|
74
|
+
`change.breadcrumb` is the only compact router. Hooks may inject its change/task,
|
|
75
|
+
role, gate, readiness, blockers, and one next action. They must not inject intent
|
|
76
|
+
bodies, transcripts, external memory, or graph payloads. Missing references,
|
|
77
|
+
digest drift, HEAD drift, or budget overflow blocks readiness.
|
|
78
|
+
|
|
79
|
+
Stable discoveries use `change.learning_propose`; they reach the baseline only
|
|
80
|
+
through approve/reject/apply transitions in `change.learning_resolve`. Unresolved
|
|
81
|
+
learning blocks convergence. Review contributes two independent axes,
|
|
82
|
+
`spec_fidelity` and `engineering_standards`; neither can replace the other.
|
|
83
|
+
|
|
84
|
+
## 5. Native host presentation
|
|
66
85
|
|
|
67
86
|
| Host | Plugin form | Workflow entry | Hook form | Collaboration companions |
|
|
68
87
|
|---|---|---|---|---|
|
|
@@ -75,9 +94,10 @@ The current host remains primary. Collaboration skills call another runtime
|
|
|
75
94
|
only when explicitly requested, use it as a read-only advisor, and return the
|
|
76
95
|
evidence to the primary host for final verification.
|
|
77
96
|
|
|
78
|
-
##
|
|
97
|
+
## 6. Hook boundary
|
|
79
98
|
|
|
80
|
-
The canonical Python hook
|
|
99
|
+
The canonical Python hook bundle contains seven executable workflow hooks plus
|
|
100
|
+
the shared read-only `context_spine.py` breadcrumb helper:
|
|
81
101
|
|
|
82
102
|
- `health_check.py` and `workflow_context.py` on session start;
|
|
83
103
|
- `active_task_context.py` before an edit;
|
|
@@ -86,7 +106,9 @@ The canonical Python hook allowlist contains seven workflow-only adapters:
|
|
|
86
106
|
- `pre_stop_check.py` at stop;
|
|
87
107
|
- `subagent_tracker.py` for bounded worker lifecycle evidence.
|
|
88
108
|
|
|
89
|
-
`
|
|
109
|
+
`workflow_context.py`, `active_task_context.py`, and `workflow_resume.py` read the
|
|
110
|
+
same DB-derived breadcrumb through `context_spine.py`. `health_check.py` and
|
|
111
|
+
`workflow_context.py` may inspect any initialized project;
|
|
90
112
|
`active_task_context.py` always protects the task projection but limits ordinary
|
|
91
113
|
edit guidance to an active workflow. Compact/stop/subagent hooks remain
|
|
92
114
|
active-workflow scoped. OpenCode natively injects baseline/change context and
|
|
@@ -95,7 +117,7 @@ protects the projection; full health inspection is available through
|
|
|
95
117
|
Generic command blocking, post-edit governance, and unrelated user hooks are not
|
|
96
118
|
copied into Ultra Builder Pro.
|
|
97
119
|
|
|
98
|
-
##
|
|
120
|
+
## 7. Memory boundary
|
|
99
121
|
|
|
100
122
|
Ultra Builder Pro has no memory MCP family, recall skill, prompt capture,
|
|
101
123
|
transcript capture, observation journal, or session-summary hook. Persistent
|
|
@@ -113,14 +135,14 @@ ultra-tools legacy-memory prune --confirm DELETE_ULTRA_LEGACY_MEMORY
|
|
|
113
135
|
|
|
114
136
|
Archive before prune; the confirmation token is intentionally required.
|
|
115
137
|
|
|
116
|
-
##
|
|
138
|
+
## 8. Project state layout
|
|
117
139
|
|
|
118
140
|
```text
|
|
119
141
|
.ultra/
|
|
120
142
|
├── state.db # authoritative SQLite store
|
|
121
143
|
├── workflow-state.json # active workflow/recovery checkpoint
|
|
122
144
|
├── changes/
|
|
123
|
-
│ ├── active/<id>/ # intent, delta, plan,
|
|
145
|
+
│ ├── active/<id>/ # intent, delta, plan, context v2, learning, verification
|
|
124
146
|
│ └── archive/ # converged packets after baseline reconciliation
|
|
125
147
|
├── tasks/ # projections and task contexts
|
|
126
148
|
├── specs/ # research/product/architecture artifacts
|
|
@@ -130,7 +152,7 @@ Archive before prune; the confirmation token is intentionally required.
|
|
|
130
152
|
└── delivery-report.json
|
|
131
153
|
```
|
|
132
154
|
|
|
133
|
-
##
|
|
155
|
+
## 9. User handbook integration
|
|
134
156
|
|
|
135
157
|
General engineering doctrine remains user-owned in `CLAUDE.md` or `AGENTS.md`.
|
|
136
158
|
Ultra Builder Pro contributes one managed section only. Preview or apply it with:
|
|
@@ -140,12 +162,12 @@ ubp-handbook preview --runtime codex
|
|
|
140
162
|
ubp-handbook apply --runtime codex
|
|
141
163
|
```
|
|
142
164
|
|
|
143
|
-
Supported runtime names are `claude`, `codex`, and `
|
|
165
|
+
Supported runtime names are `claude`, `codex`, `opencode`, and `kimi`. Apply creates a
|
|
144
166
|
timestamped backup, replaces only the marked block, and can migrate the old
|
|
145
167
|
Codex `## Ultra Builder Pro Runtime Contract` section without touching the next
|
|
146
168
|
user section. Plugin adapters themselves do not silently overwrite handbooks.
|
|
147
169
|
|
|
148
|
-
##
|
|
170
|
+
## 10. Verification
|
|
149
171
|
|
|
150
172
|
Run `npm run test:all`, `python3 -m pytest hooks/tests -q`, and `npm audit`.
|
|
151
173
|
Adapter tests assert the allowlisted assets, native manifests, hook boundary,
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -53,7 +53,7 @@ the supported hosts share.
|
|
|
53
53
|
| Layer | Role | Form |
|
|
54
54
|
|--------------|----------------------------------------------|-------------------------------------------------------|
|
|
55
55
|
| **skill** | knowledge carrier; tells the runtime *what to do* | `skills/<name>/SKILL.md` discovered natively by all supported runtimes |
|
|
56
|
-
| **MCP** | authoritative workflow-state API
|
|
56
|
+
| **MCP** | authoritative workflow-state and Context Spine API | stdio MCP server exposing 32 live tools across task/session/change/system/plan families in [`spec/mcp-tools.yaml`](../spec/mcp-tools.yaml) |
|
|
57
57
|
| **CLI** | explicit initialization, recovery, diagnostics, and orchestration | `ultra-tools` / `ubp-orchestrator`; only commands listed by `--help` are executable (see [`spec/cli-protocol.md`](../spec/cli-protocol.md)) |
|
|
58
58
|
|
|
59
59
|
Why three: skills give us behavior portability across runtimes; MCP gives
|
|
@@ -70,7 +70,7 @@ RPC, and host-native loaders own resolution and invocation.
|
|
|
70
70
|
|
|
71
71
|
All durable Ultra workflow state lives in one SQLite file with WAL enabled. Schema is
|
|
72
72
|
fixed in [`spec/schemas/state-db.sql`](../spec/schemas/state-db.sql) and
|
|
73
|
-
covers
|
|
73
|
+
covers sixteen tables:
|
|
74
74
|
|
|
75
75
|
| Table | Holds | Phase |
|
|
76
76
|
|--------------------|---------------------------------------------------|-------|
|
|
@@ -84,7 +84,8 @@ covers fifteen tables:
|
|
|
84
84
|
| `circuit_breaker` | bounded retry and halt state for failed tasks | 5 |
|
|
85
85
|
| `changes` | continuous feature/fix/incident lifecycle | 8C |
|
|
86
86
|
| `artifacts` | intent/context/verification artifact registry | 8C |
|
|
87
|
-
| `context_snapshots`|
|
|
87
|
+
| `context_snapshots`| role/gate context, readiness, budget, execution seam, hashes, git head, provider refs | 10 |
|
|
88
|
+
| `spec_learning_candidates` | approval-gated implementation discoveries for baseline convergence | 10 |
|
|
88
89
|
| `trace_links` | task/spec/change traceability | 8C |
|
|
89
90
|
| `incidents` | structured runtime failures and resolutions | 8C |
|
|
90
91
|
| `projection_jobs` | durable projection outbox/retry state | 8C |
|
|
@@ -101,7 +102,8 @@ Two rules make this work:
|
|
|
101
102
|
2. **Single writer for mutable tables, multi-writer for `events`.**
|
|
102
103
|
`.ultra/state.db` opens in WAL with `busy_timeout=5000`. The MCP
|
|
103
104
|
server holds the single writer connection for `tasks`, `sessions`,
|
|
104
|
-
`
|
|
105
|
+
`changes`, `context_snapshots`, `spec_learning_candidates`, `telemetry`,
|
|
106
|
+
`specs_refs`, and `migration_history`; the CLI calls
|
|
105
107
|
those tools over stdio rather than opening its own writer. The
|
|
106
108
|
`events` table is append-only and explicitly multi-writer — CLI and
|
|
107
109
|
orchestrator processes append directly to it under the same WAL +
|
|
@@ -133,7 +135,8 @@ active -> blocked -> active -> ready -> archived
|
|
|
133
135
|
| |
|
|
134
136
|
├─ intent + delta + plan └─ verification + baseline reconciliation
|
|
135
137
|
├─ linked tasks
|
|
136
|
-
|
|
138
|
+
├─ context-manifest v2 (role + gate + readiness + execution seam)
|
|
139
|
+
└─ spec-learning candidates (proposed -> approved/rejected -> applied)
|
|
137
140
|
```
|
|
138
141
|
|
|
139
142
|
`quick`, `standard`, `major`, and `incident` kinds require different evidence,
|
|
@@ -141,6 +144,29 @@ but all require completed linked tasks, current context, declared documentation
|
|
|
141
144
|
impact, and no open incident. Memory and graph payloads never enter Ultra;
|
|
142
145
|
`context_snapshots.provider_refs_json` stores metadata references only.
|
|
143
146
|
|
|
147
|
+
### Context Spine
|
|
148
|
+
|
|
149
|
+
Context Manifest v2 is the handoff contract between planning, implementation,
|
|
150
|
+
checking, review, convergence, and recovery. Each snapshot records:
|
|
151
|
+
|
|
152
|
+
- one role (`plan`, `implement`, `check`, or `review`) and lifecycle gate;
|
|
153
|
+
- required context references with local digests and reasons;
|
|
154
|
+
- readiness blockers for missing/stale references or budget overflow;
|
|
155
|
+
- a fresh-context budget (12 files / about 12k tokens / 40% by default);
|
|
156
|
+
- an execution contract (`slice_kind`, public seam, exact verification command);
|
|
157
|
+
- one deterministic next action.
|
|
158
|
+
|
|
159
|
+
`change.breadcrumb` derives the compact current position from state.db. Session,
|
|
160
|
+
edit, and resume hooks inject only this breadcrumb, never the intent body,
|
|
161
|
+
provider content, or a conversation summary. A changed git HEAD marks the
|
|
162
|
+
snapshot stale and routes back to `change.context`.
|
|
163
|
+
|
|
164
|
+
`change.learning_propose` records a durable implementation discovery without
|
|
165
|
+
silently rewriting the baseline. `change.learning_resolve` enforces approval,
|
|
166
|
+
rejection, and applied transitions. Proposed or merely approved candidates block
|
|
167
|
+
convergence, so daily work cannot leave accepted behavior stranded in chat or a
|
|
168
|
+
delta packet.
|
|
169
|
+
|
|
144
170
|
`incident` is the canonical debug lane. Creating one also registers a durable
|
|
145
171
|
`diagnosis.md` artifact with five mandatory sections: reproduction, hypotheses,
|
|
146
172
|
root cause, regression test, and recovery. Convergence validates the artifact
|
package/docs/PLAN.zh-CN.md
CHANGED
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
> Kimi Code 采用各自 native plugin 呈现,其他旧 runtime adapter 已退役。下文所有与此冲突的
|
|
11
11
|
> memory/hindsight 章节仅作为历史实施记录,不再是当前需求或运行时契约。
|
|
12
12
|
|
|
13
|
-
**当前状态**:v0.
|
|
14
|
-
schema
|
|
13
|
+
**当前状态**:v0.10.0;四宿主 native plugin、32 个 live MCP tool、
|
|
14
|
+
schema 10.0(16 表)、Context Spine v2 / breadcrumb / spec-learning、持续
|
|
15
|
+
change/context/convergence/doctor、incident debug lane、
|
|
15
16
|
checkpoint 恢复消费与跨宿主安装 provenance/doctor 已落地;npm 发布由
|
|
16
17
|
`v*.*.*` tag 触发。以下 Phase 0-9 正文保留为 2026-04 的实施历史,不再作为
|
|
17
18
|
当前 runtime 或发布状态的事实来源。
|