Skip to content

Commit 0f6f925

Browse files
committed
feat: add the Ultra Context Spine
Daily maintenance could drift away from the original specification because role context, task freshness, verification seams, and implementation discoveries were not one durable contract. Hooks also injected broad workflow prose while projection-only hosts could accept stale manifests. This release adds Context Manifest v2, schema 10.0, deterministic breadcrumbs, approval-gated specification learning, and 32 live MCP contracts. It gives every linked task a bounded role/gate packet, required references and digests, a public seam, an exact verification command, and fail-closed readiness. Convergence now requires current task contexts, documentation/spec evidence, exact test seams, and independent spec-fidelity and engineering-standards review. The seven daily execution workflows are compressed from 1,922 to 758 lines and all twelve public workflows are host-invocable. Claude Code, Codex, OpenCode, and Kimi Code receive native Context Spine rendering; OpenCode rejects legacy or HEAD-stale projections. A shared read-only hook helper replaces duplicated broad context injection. Compatibility remains additive: the 9.1 to 10.0 migration preserves durable rows and foreign keys; external providers continue to own memory and code graphs; Gemini and RTK remain absent from active runtime, prompt, installer, and documentation surfaces. Rollback is package reinstall to 0.9.0; databases retain additive columns/table and should be backed up before any manual downgrade. Verification: npm run verify:release (144 Node tests, 21 hook tests, npm audit with 0 vulnerabilities); npm pack --dry-run --json (222 files, all new runtime assets present); git diff --cached --check; MCP system.doctor reports healthy with SQLite integrity ok, zero projection lag, zero open incidents, and external provider ownership.
1 parent 825012d commit 0f6f925

70 files changed

Lines changed: 2667 additions & 1954 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

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+
1057
## [0.9.0] — 2026-07-17
1158

1259
### Added

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ authoritative `.ultra/state.db`.
88

99
<div align="center">
1010

11-
[![Version](https://img.shields.io/badge/version-0.9.0-blue)](./CHANGELOG.md)
11+
[![Version](https://img.shields.io/badge/version-0.10.0-blue)](./CHANGELOG.md)
1212
[![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](#verification)
1313
[![License: MIT](https://img.shields.io/badge/license-MIT-green)](./LICENSE)
1414
[![Node](https://img.shields.io/badge/node-%E2%89%A522-informational)](./package.json)
@@ -36,8 +36,10 @@ authoritative `.ultra/state.db`.
3636
- **Runs real PRDs end-to-end.** `task.parse_prd``lib/topo.cjs` waves →
3737
`.ultra/execution-plan.json` → parallel worktree sessions → auto-merge back.
3838
- **Keeps daily work convergent.** `/ultra-change` creates a bounded delta and
39-
context manifest; `/ultra-deliver` requires docs/test/review evidence and
40-
baseline reconciliation before archive. `/ultra-doctor` reports incidents,
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,
4143
projection lag, orphan sessions, and backup-first mechanical recovery.
4244
Incident changes additionally require a five-section `diagnosis.md` covering
4345
reproduction, hypotheses, root cause, regression test, and recovery.
@@ -91,7 +93,7 @@ for per-runtime capabilities.
9193
The layers share one `.ultra/state.db`, but the CLI is not a mirror of every MCP
9294
tool: continuous change mutations are MCP-only and fail closed. See
9395
[`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) for the full contract and
94-
[`spec/cli-protocol.md`](./spec/cli-protocol.md) for the 29 live contracts.
96+
[`spec/cli-protocol.md`](./spec/cli-protocol.md) for the 32 live contracts.
9597
Review, impact discovery, skill resolution, and user interaction stay on each
9698
Host's native surfaces; the generated Codex capability map documents those
9799
replacements without advertising non-existent MCP tools.
@@ -137,8 +139,11 @@ ultra-tools system doctor
137139

138140
Or let the skills drive it. In Codex, invoke
139141
`$ultra-builder-pro:ultra-plan``$ultra-builder-pro:ultra-dev`
140-
`$ultra-builder-pro:ultra-status`; after the baseline is delivered, daily work
141-
starts with `$ultra-builder-pro:ultra-change`. Kimi uses the corresponding
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
142147
`/ultra-builder-pro:ultra-*` namespace. Other runtimes retain their native
143148
command form.
144149

adapters/_shared/codex-assets.cjs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,16 @@ and does not require an interactive MCP or legacy CLI menu.
467467
}
468468

469469
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+
);
470480
text = text.replaceAll('### Phase 3: Background Execution', '### Phase 3: Parallel Native Agent Execution');
471481
text = text.replaceAll('**Step 4b: Launch coordinator in background:**', '**Step 4b: Delegate coordination:**');
472482
text = text.replaceAll('in **background mode** (`native parallel execution`)', 'concurrently with native Codex subagent orchestration');

adapters/_shared/handbook.cjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ function renderHandbook(runtime) {
6060
`- Collaboration boundary: ${value.collaboration}. The current host remains primary and owns final verification.`,
6161
`- Coordination boundary: ${value.coordination}.`,
6262
'- 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.',
6365
'- 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.',
6466
'- 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.',
6567
'- 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.',

adapters/_shared/runtime-assets.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const RETIRED_SKILLS = Object.freeze([
5050

5151
const WORKFLOW_HOOK_FILES = Object.freeze([
5252
'active_task_context.py',
53+
'context_spine.py',
5354
'health_check.py',
5455
'pre_stop_check.py',
5556
'subagent_tracker.py',

adapters/_shared/tests/handbook.test.cjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ test('renders one common contract with host-native invocation syntax', () => {
3333
assert.match(rendered, /ultra-change/);
3434
assert.match(rendered, /ultra-doctor/);
3535
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/);
3639
assert.doesNotMatch(rendered, /\.ultra\/memory|memory\.retain|memory\.recall/);
3740
assert.match(rendered, new RegExp(BEGIN_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
3841
assert.match(rendered, new RegExp(END_MARKER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));

adapters/_shared/tests/runtime-assets.test.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ test('code-review-expert and rule skills are internal, never implicitly exposed'
8383
test('workflow hook allowlist contains no memory, prompt capture, or generic policy hook', () => {
8484
assert.deepEqual(WORKFLOW_HOOK_FILES, [
8585
'active_task_context.py',
86+
'context_spine.py',
8687
'health_check.py',
8788
'pre_stop_check.py',
8889
'subagent_tracker.py',

adapters/kimi.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ bundled files are bounded prompt templates, not separately registered host agent
219219
'Read `SUMMARY.json` only after this validation succeeds.',
220220
].join('\n');
221221
text = text.replace(
222-
/### Phase 3:[\s\S]*?### Phase 5: Report to User/,
223-
`${executionContract}\n\n### Phase 5: Report to User`,
222+
/#{2,3} Phase 3:[\s\S]*?#{2,3} Phase 5: Report to User/,
223+
`${executionContract}\n\n## Phase 5: Report to User`,
224224
);
225225
text = text.replaceAll('Background Execution', 'Reviewer Execution');
226226
text = text.replaceAll('Wait & Coordinate', 'Validate & Coordinate');

adapters/opencode.js

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -336,25 +336,26 @@ function pluginSource() {
336336
return `// Managed by Ultra Builder Pro. Rebuild through the adapter; do not edit in place.
337337
import fs from "node:fs";
338338
import path from "node:path";
339+
import { execFileSync } from "node:child_process";
339340
340341
const TERMINAL = new Set(["committed", "completed", "done", "cancelled"]);
341342
const TASKS_PROJECTION = ".ultra/tasks/tasks.json";
342-
const PROVIDER_FIELDS = new Set(["provider", "project", "reference", "revision", "indexed_head", "status"]);
343-
344-
function providerMetadata(value) {
345-
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
346-
const clean = {};
347-
for (const [kind, ref] of Object.entries(value)) {
348-
if (!["memory", "code_graph"].includes(kind) || !ref || typeof ref !== "object" || Array.isArray(ref)) continue;
349-
clean[kind] = Object.fromEntries(Object.entries(ref).filter(([key]) => PROVIDER_FIELDS.has(key)));
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;
350351
}
351-
return clean;
352352
}
353353
354354
function findUltraContext(directory) {
355355
let current = path.resolve(directory);
356356
while (true) {
357357
const ultra = path.join(current, ".ultra");
358+
const currentHead = gitHead(current);
358359
const workflowFile = path.join(ultra, "workflow-state.json");
359360
const statePresent = fs.existsSync(path.join(ultra, "state.db"));
360361
let workflow = null;
@@ -375,11 +376,36 @@ function findUltraContext(directory) {
375376
if (!fs.existsSync(manifestPath)) continue;
376377
try {
377378
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
378-
if (manifest?.change) changes.push({
379-
...manifest.change,
380-
providers: providerMetadata(manifest.providers),
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,
381406
manifestPath: path.relative(current, manifestPath),
382407
});
408+
}
383409
} catch (error) {
384410
process.stderr.write(\`[ultra-builder-pro] cannot read \${manifestPath}: \${error.message}\\n\`);
385411
}
@@ -410,13 +436,16 @@ function contextText(active) {
410436
if (active.changes.length > 0) {
411437
const change = active.changes[0];
412438
lines.push(
413-
"[Ultra continuous change]",
439+
"[Ultra context spine]",
414440
\`Project: \${active.root}\`,
415441
\`Change: \${change.id} (\${change.kind ?? "unknown"}, \${change.status ?? "active"})\`,
416-
\`Title: \${change.title ?? "unknown"}\`,
417-
\`Intent: \${change.intent ?? "unknown"}\`,
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}\`,
418448
\`Context manifest: \${change.manifestPath}\`,
419-
\`External providers (metadata references only): \${JSON.stringify(change.providers)}\`,
420449
);
421450
if (active.changes.length > 1) lines.push(
422451
\`Active change count: \${active.changes.length}; resolve scope with change.list before mutation.\`,
@@ -429,7 +458,7 @@ function contextText(active) {
429458
"No active continuous change. Start daily work with the ultra-change workflow.",
430459
);
431460
}
432-
lines.push("Authority: .ultra/state.db; generated JSON/Markdown are projections or artifacts.");
461+
lines.push("Authority: .ultra/state.db; JSON/Markdown remain projections or evidence artifacts.");
433462
return lines.join("\\n");
434463
}
435464

adapters/tests/claude.test.cjs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ test('install builds the Claude-native plugin from the explicit Ultra allowlist'
3636

3737
const hooks = JSON.parse(fs.readFileSync(path.join(target, 'hooks', 'hooks.json'), 'utf8'));
3838
const serialized = JSON.stringify(hooks);
39-
for (const name of WORKFLOW_HOOK_FILES) assert.match(serialized, new RegExp(name.replace('.', '\\.')));
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')));
4043
assert.doesNotMatch(serialized, /memory|recall|journal|observation_capture|user_prompt_capture|block_dangerous|post_edit_guard/);
4144
assert.match(serialized, /\$\{CLAUDE_PLUGIN_ROOT\}/);
4245
} finally {

0 commit comments

Comments
 (0)