ultra-builder-pro-cli 0.13.0 → 0.15.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/.ultra-template/docs/research/README.md +11 -7
- package/.ultra-template/specs/architecture.md +36 -36
- package/.ultra-template/specs/discovery.md +46 -33
- package/.ultra-template/specs/product.md +38 -29
- package/.ultra-template/specs/research-distillate.md +48 -0
- package/.ultra-template/tasks/contexts/TEMPLATE.md +11 -28
- package/CHANGELOG.md +86 -1
- package/README.md +40 -26
- package/adapters/_shared/codex-assets.cjs +29 -165
- package/adapters/_shared/file-ops.cjs +6 -2
- package/adapters/_shared/runtime-assets.cjs +1 -0
- package/adapters/claude.js +3 -2
- package/adapters/codex.js +1 -0
- package/adapters/kimi.js +3 -2
- package/adapters/opencode.js +33 -122
- package/agents/review-coordinator.md +9 -6
- package/docs/AGENT-CONTEXT.md +21 -11
- package/docs/ARCHITECTURE.md +41 -13
- package/docs/PLAN.zh-CN.md +3 -2
- package/docs/ROADMAP.md +9 -5
- package/docs/RUNTIME-COMPAT-MATRIX.md +6 -6
- package/docs/STATE-DB-ACCESS-POLICY.md +27 -25
- package/docs/WORKFLOW-LIFECYCLE.md +266 -0
- package/hooks/README.md +6 -1
- package/hooks/active_task_context.py +22 -50
- package/hooks/context_spine.py +61 -332
- package/hooks/health_check.py +29 -18
- package/hooks/pre_stop_check.py +28 -33
- package/hooks/subagent_tracker.py +54 -85
- package/hooks/workflow_checkpoint.py +39 -47
- package/hooks/workflow_context.py +4 -9
- package/hooks/workflow_resume.py +48 -128
- package/mcp-server/breadcrumb.cjs +22 -0
- package/mcp-server/hook-event.cjs +34 -0
- package/mcp-server/lib/archive-journal.cjs +17 -5
- package/mcp-server/lib/baseline-workflow.cjs +158 -18
- package/mcp-server/lib/change-workflow.cjs +550 -85
- package/mcp-server/lib/context-spine.cjs +242 -24
- package/mcp-server/lib/doctor.cjs +33 -3
- package/mcp-server/lib/init-project.cjs +210 -16
- package/mcp-server/lib/plan-store.cjs +4 -1
- package/mcp-server/lib/prd-parser.cjs +14 -41
- package/mcp-server/lib/pricing.cjs +7 -10
- package/mcp-server/lib/project-breadcrumb.cjs +145 -0
- package/mcp-server/lib/projector.cjs +57 -3
- package/mcp-server/lib/spec-learning.cjs +79 -2
- package/mcp-server/lib/state-db.cjs +145 -8
- package/mcp-server/lib/state-ops.cjs +160 -9
- package/mcp-server/lib/task-expander.cjs +9 -38
- package/mcp-server/lib/workflow-state.cjs +2292 -0
- package/mcp-server/server.cjs +182 -50
- package/orchestrator/daemon.cjs +1 -2
- package/orchestrator/dispatch-rules.cjs +5 -31
- package/orchestrator/planner/plan-builder.cjs +21 -12
- package/package.json +14 -6
- package/skills/cc-collab/SKILL.md +2 -2
- package/skills/codex-collab/SKILL.md +2 -3
- package/skills/ultra-change/SKILL.md +53 -68
- package/skills/ultra-change/references/change-contract.md +36 -0
- package/skills/ultra-deliver/SKILL.md +69 -79
- package/skills/ultra-deliver/references/baseline-reconciliation.md +20 -0
- package/skills/ultra-dev/SKILL.md +54 -59
- package/skills/ultra-doctor/SKILL.md +40 -71
- package/skills/ultra-init/SKILL.md +96 -103
- package/skills/ultra-init/references/brownfield-adoption.md +2 -2
- package/skills/ultra-plan/SKILL.md +79 -75
- package/skills/ultra-plan/references/semantic-preflight.md +18 -0
- package/skills/ultra-research/SKILL.md +87 -60
- package/skills/ultra-research/references/00-problem-validation.md +36 -0
- package/skills/ultra-research/references/01-opportunity-discovery.md +33 -0
- package/skills/ultra-research/references/02-market-assessment.md +30 -0
- package/skills/ultra-research/references/03-competitive-landscape.md +31 -0
- package/skills/ultra-research/references/04-product-strategy.md +32 -0
- package/skills/ultra-research/references/05-assumptions-validation.md +31 -0
- package/skills/ultra-research/references/10-user-personas.md +29 -0
- package/skills/ultra-research/references/11-user-scenarios.md +28 -0
- package/skills/ultra-research/references/20-user-stories.md +30 -0
- package/skills/ultra-research/references/21-features-scope.md +29 -0
- package/skills/ultra-research/references/22-success-metrics.md +28 -0
- package/skills/ultra-research/references/30-architecture-context.md +30 -0
- package/skills/ultra-research/references/31-solution-strategy.md +28 -0
- package/skills/ultra-research/references/32-building-blocks.md +28 -0
- package/skills/ultra-research/references/40-deployment.md +29 -0
- package/skills/ultra-research/references/41-quality-risks.md +28 -0
- package/skills/ultra-research/references/99-synthesis.md +35 -0
- package/skills/ultra-review/SKILL.md +57 -73
- package/skills/ultra-review/references/review-modes.md +27 -0
- package/skills/ultra-review/references/unified-schema.md +25 -0
- package/skills/ultra-review/scripts/review_wait.py +40 -3
- package/skills/ultra-status/SKILL.md +41 -45
- package/skills/ultra-test/SKILL.md +48 -55
- package/skills/ultra-verify/SKILL.md +22 -31
- package/skills/ultra-verify/references/{confidence-system.md → evidence-status.md} +3 -3
- package/skills/ultra-verify/scripts/verify_wait.py +54 -54
- package/spec/cli-protocol.md +7 -2
- package/spec/mcp-tools.yaml +422 -73
- package/spec/schemas/baseline-reconciliation.v1.schema.json +73 -0
- package/spec/schemas/delivery-report.v1.schema.json +111 -0
- package/spec/schemas/mcp-tools.schema.json +1 -1
- package/spec/schemas/state-db.sql +82 -1
- package/spec/schemas/tasks.v4.5.schema.json +8 -1
- package/spec/schemas/test-report.v1.schema.json +194 -0
- package/ultra-tools/cli.cjs +1 -1
- package/ultra-tools/commands/migrate.cjs +3 -3
- package/ultra-tools/commands/status.cjs +213 -12
- package/.ultra-template/delivery-report.json +0 -9
- package/.ultra-template/test-report.json +0 -8
- package/adapters/_shared/tests/file-ops.test.cjs +0 -54
- package/adapters/_shared/tests/frontmatter.test.cjs +0 -47
- package/adapters/_shared/tests/handbook.test.cjs +0 -101
- package/adapters/_shared/tests/path-rewrite.test.cjs +0 -60
- package/adapters/_shared/tests/provenance.test.cjs +0 -133
- package/adapters/_shared/tests/resolve-target.test.cjs +0 -75
- package/adapters/_shared/tests/runtime-assets.test.cjs +0 -131
- package/adapters/_shared/tests/settings-merge.test.cjs +0 -85
- package/adapters/_shared/tests/validate.test.cjs +0 -30
- package/adapters/tests/claude.test.cjs +0 -137
- package/adapters/tests/codex-hook.test.cjs +0 -115
- package/adapters/tests/codex.test.cjs +0 -423
- package/adapters/tests/kimi-hook.test.cjs +0 -122
- package/adapters/tests/kimi.test.cjs +0 -259
- package/adapters/tests/opencode.test.cjs +0 -371
- package/mcp-server/lib/archive-journal.test.cjs +0 -35
- package/mcp-server/lib/baseline-workflow.test.cjs +0 -477
- package/mcp-server/lib/change-workflow.test.cjs +0 -971
- package/mcp-server/lib/context-spine.test.cjs +0 -93
- package/mcp-server/lib/doctor.test.cjs +0 -148
- package/mcp-server/lib/llm-client.cjs +0 -157
- package/mcp-server/lib/llm-client.test.cjs +0 -156
- package/mcp-server/lib/plan-store.test.cjs +0 -132
- package/mcp-server/lib/prd-parser.test.cjs +0 -171
- package/mcp-server/lib/runtime-state.test.cjs +0 -92
- package/mcp-server/lib/spec-learning.test.cjs +0 -67
- package/mcp-server/lib/state-db.test.cjs +0 -314
- package/mcp-server/lib/task-expander.test.cjs +0 -324
- package/mcp-server/lib/topo.test.cjs +0 -158
- package/mcp-server/tests/circuit-breaker.test.cjs +0 -125
- package/mcp-server/tests/commit-hash-flow.test.cjs +0 -117
- package/mcp-server/tests/concurrency-update.test.cjs +0 -96
- package/mcp-server/tests/concurrency.test.cjs +0 -112
- package/mcp-server/tests/db-ops.test.cjs +0 -121
- package/mcp-server/tests/init-project.test.cjs +0 -393
- package/mcp-server/tests/migrate.test.cjs +0 -232
- package/mcp-server/tests/projector.test.cjs +0 -162
- package/mcp-server/tests/server-contract.test.cjs +0 -1471
- package/mcp-server/tests/staleness.test.cjs +0 -190
- package/mcp-server/tests/state-db.test.cjs +0 -106
- package/mcp-server/tests/state-ops.test.cjs +0 -449
- package/mcp-server/tests/tagged-tasks.test.cjs +0 -163
- package/mcp-server/tests/telemetry.test.cjs +0 -219
- package/orchestrator/planner/expand-prompt.cjs +0 -62
- package/orchestrator/planner/expand-prompt.test.cjs +0 -52
- package/orchestrator/planner/plan-builder.test.cjs +0 -165
- package/orchestrator/planner/prd-prompt.cjs +0 -44
- package/orchestrator/planner/prd-prompt.test.cjs +0 -30
- package/orchestrator/tests/auto-merge.test.cjs +0 -216
- package/orchestrator/tests/daemon.test.cjs +0 -311
- package/orchestrator/tests/dispatch-rules.test.cjs +0 -124
- package/orchestrator/tests/parallel-orchestrator.test.cjs +0 -233
- package/orchestrator/tests/phase-8b-gate.test.cjs +0 -183
- package/orchestrator/tests/recovery.test.cjs +0 -190
- package/orchestrator/tests/session-runner.test.cjs +0 -314
- package/orchestrator/tests/skill-miner.test.cjs +0 -147
- package/orchestrator/tests/worktree-manager.test.cjs +0 -148
- package/skills/ultra-research/references/architecture.md +0 -38
- package/skills/ultra-research/references/problem-product.md +0 -39
- package/skills/ultra-research/references/quality-delivery.md +0 -34
- package/skills/ultra-research/references/synthesis.md +0 -38
- package/skills/ultra-research/references/users-scope.md +0 -40
- package/ultra-tools/legacy-memory.test.cjs +0 -80
- package/ultra-tools/status-command.test.cjs +0 -181
- package/ultra-tools/system-command.test.cjs +0 -309
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# Quality and delivery research
|
|
2
|
-
|
|
3
|
-
Read this reference when acceptance depends on operational risk, security, migration,
|
|
4
|
-
deployment, recovery, or release constraints.
|
|
5
|
-
|
|
6
|
-
## Risk-driven quality
|
|
7
|
-
|
|
8
|
-
Identify failures that would materially affect users, data, authorization, money,
|
|
9
|
-
availability, or recoverability. For each relevant risk, record:
|
|
10
|
-
|
|
11
|
-
- trigger and affected boundary;
|
|
12
|
-
- expected behavior and observable signal;
|
|
13
|
-
- prevention, detection, and recovery;
|
|
14
|
-
- verification method and owner.
|
|
15
|
-
|
|
16
|
-
Apply security, performance, accessibility, and resilience checks according to the
|
|
17
|
-
actual product surface. Do not impose generic categories or coverage percentages when
|
|
18
|
-
they do not define acceptance.
|
|
19
|
-
|
|
20
|
-
## Delivery path
|
|
21
|
-
|
|
22
|
-
Capture the environments, configuration authority, migration order, compatibility
|
|
23
|
-
window, health and readiness checks, observability, rollout, rollback, and operator
|
|
24
|
-
actions needed for this product.
|
|
25
|
-
|
|
26
|
-
Prefer existing repository and platform workflows. Add a new service, dependency, or
|
|
27
|
-
deployment mechanism only when the accepted architecture requires it.
|
|
28
|
-
|
|
29
|
-
## Record the result
|
|
30
|
-
|
|
31
|
-
Update the architecture and product specifications with testable quality scenarios,
|
|
32
|
-
delivery constraints, failure behavior, recovery evidence, and release acceptance.
|
|
33
|
-
Keep exact volatile commands in the repository's runbook when they are operational
|
|
34
|
-
procedure rather than durable architecture.
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# Baseline synthesis and validation
|
|
2
|
-
|
|
3
|
-
Read this reference after the selected research lanes are complete.
|
|
4
|
-
|
|
5
|
-
## Traceability check
|
|
6
|
-
|
|
7
|
-
Verify that each accepted outcome has:
|
|
8
|
-
|
|
9
|
-
1. supporting problem, user, or constraint evidence;
|
|
10
|
-
2. an explicit decision and scope boundary;
|
|
11
|
-
3. testable acceptance behavior;
|
|
12
|
-
4. a compatible architecture or delivery path;
|
|
13
|
-
5. a named unresolved blocker when any link is missing.
|
|
14
|
-
|
|
15
|
-
Check the reverse direction as well: every proposed component, dependency, and quality
|
|
16
|
-
requirement must support an accepted outcome or constraint.
|
|
17
|
-
|
|
18
|
-
## Consistency check
|
|
19
|
-
|
|
20
|
-
- Use one term for each actor, boundary, and authoritative data source.
|
|
21
|
-
- Remove duplicate narratives and stale alternatives.
|
|
22
|
-
- Keep source links next to the claims they support.
|
|
23
|
-
- Separate durable decisions from volatile external facts.
|
|
24
|
-
- Preserve explicit non-goals and rejected alternatives only when they prevent a
|
|
25
|
-
likely future mistake.
|
|
26
|
-
|
|
27
|
-
## Handoff
|
|
28
|
-
|
|
29
|
-
Report:
|
|
30
|
-
|
|
31
|
-
- validated scope and decisive evidence;
|
|
32
|
-
- specification files changed;
|
|
33
|
-
- accepted decisions and material uncertainty;
|
|
34
|
-
- blockers, or an explicit statement that none remain;
|
|
35
|
-
- one next action.
|
|
36
|
-
|
|
37
|
-
Do not create a parallel summary document solely for model consumption. The planning
|
|
38
|
-
workflow should read the smallest authoritative baseline sections it needs.
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Users, behavior, and scope
|
|
2
|
-
|
|
3
|
-
Read this reference when actors, workflows, acceptance, boundaries, or success signals
|
|
4
|
-
are unclear.
|
|
5
|
-
|
|
6
|
-
## Actors and scenarios
|
|
7
|
-
|
|
8
|
-
Model only actors supported by evidence. Prefer roles and jobs over fictional persona
|
|
9
|
-
biographies. For each material scenario, capture:
|
|
10
|
-
|
|
11
|
-
- trigger and preconditions;
|
|
12
|
-
- actor goal;
|
|
13
|
-
- main flow and system response;
|
|
14
|
-
- important failure or recovery path;
|
|
15
|
-
- observable completion signal.
|
|
16
|
-
|
|
17
|
-
## Acceptance and scope
|
|
18
|
-
|
|
19
|
-
Turn scenarios into testable behavior. Each requirement should name the observable
|
|
20
|
-
outcome, relevant constraints, and how success can be verified.
|
|
21
|
-
|
|
22
|
-
Define:
|
|
23
|
-
|
|
24
|
-
- included behavior required for the accepted outcome;
|
|
25
|
-
- explicit non-goals;
|
|
26
|
-
- dependencies and assumptions;
|
|
27
|
-
- deferred work only when a real sequencing decision exists.
|
|
28
|
-
|
|
29
|
-
Avoid arbitrary release buckets or a fixed number of stories. Scope follows the
|
|
30
|
-
validated outcome and delivery constraint.
|
|
31
|
-
|
|
32
|
-
## Success signals
|
|
33
|
-
|
|
34
|
-
Choose metrics only when they will guide a decision. Define the event, population,
|
|
35
|
-
window, source, owner, and interpretation. Do not invent target values; record a
|
|
36
|
-
baseline, a user-approved target, or the experiment needed to establish one.
|
|
37
|
-
|
|
38
|
-
Update the product specification with actors, scenarios, acceptance, scope, non-goals,
|
|
39
|
-
and decision-relevant success signals. Keep business facts in their source system and
|
|
40
|
-
link them rather than copying volatile datasets into the specification.
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { test } = require('node:test');
|
|
4
|
-
const assert = require('node:assert/strict');
|
|
5
|
-
const fs = require('node:fs');
|
|
6
|
-
const os = require('node:os');
|
|
7
|
-
const path = require('node:path');
|
|
8
|
-
const Database = require('better-sqlite3');
|
|
9
|
-
|
|
10
|
-
const legacy = require('./commands/legacy-memory.cjs');
|
|
11
|
-
|
|
12
|
-
function fixture() {
|
|
13
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-legacy-memory-'));
|
|
14
|
-
const ultra = path.join(root, '.ultra');
|
|
15
|
-
fs.mkdirSync(path.join(ultra, 'memory'), { recursive: true });
|
|
16
|
-
fs.writeFileSync(path.join(ultra, 'memory', 'memory.db'), 'legacy-hook-store');
|
|
17
|
-
const db = new Database(path.join(ultra, 'state.db'));
|
|
18
|
-
db.exec(`
|
|
19
|
-
CREATE TABLE memory_entries (
|
|
20
|
-
id INTEGER PRIMARY KEY, kind TEXT, content TEXT, task_id TEXT,
|
|
21
|
-
session_id TEXT, tag TEXT, source TEXT, ts TEXT
|
|
22
|
-
);
|
|
23
|
-
INSERT INTO memory_entries
|
|
24
|
-
(kind, content, task_id, session_id, tag, source, ts)
|
|
25
|
-
VALUES ('decision', 'legacy decision', 't1', 's1', 'arch', 'test', '2026-01-01T00:00:00Z');
|
|
26
|
-
`);
|
|
27
|
-
db.close();
|
|
28
|
-
return root;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
test('inspect reports both legacy stores without mutating them', () => {
|
|
32
|
-
const root = fixture();
|
|
33
|
-
try {
|
|
34
|
-
const result = legacy.inspectLegacy(root);
|
|
35
|
-
assert.equal(result.hook_memory_dir, true);
|
|
36
|
-
assert.equal(result.state_memory_table, true);
|
|
37
|
-
assert.equal(result.state_memory_entries, 1);
|
|
38
|
-
assert.ok(fs.existsSync(path.join(root, '.ultra', 'memory', 'memory.db')));
|
|
39
|
-
} finally {
|
|
40
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
test('archive exports legacy state rows and copies hook memory without deletion', () => {
|
|
44
|
-
const root = fixture();
|
|
45
|
-
try {
|
|
46
|
-
const result = legacy.archiveLegacy(root, { timestamp: '2026-07-15T00-00-00-000Z' });
|
|
47
|
-
assert.equal(result.archived, true);
|
|
48
|
-
assert.ok(fs.existsSync(path.join(result.archive_dir, 'hook-memory', 'memory.db')));
|
|
49
|
-
const exported = JSON.parse(fs.readFileSync(path.join(result.archive_dir, 'state-memory-entries.json'), 'utf8'));
|
|
50
|
-
assert.equal(exported.entries.length, 1);
|
|
51
|
-
assert.equal(exported.entries[0].content, 'legacy decision');
|
|
52
|
-
assert.ok(fs.existsSync(path.join(root, '.ultra', 'memory', 'memory.db')));
|
|
53
|
-
const db = new Database(path.join(root, '.ultra', 'state.db'), { readonly: true });
|
|
54
|
-
assert.ok(db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'memory_entries'").get());
|
|
55
|
-
db.close();
|
|
56
|
-
} finally {
|
|
57
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test('prune requires an exact confirmation and archives before deleting', () => {
|
|
62
|
-
const root = fixture();
|
|
63
|
-
try {
|
|
64
|
-
assert.throws(() => legacy.pruneLegacy(root, { confirm: 'yes' }), /DELETE_ULTRA_LEGACY_MEMORY/);
|
|
65
|
-
assert.ok(fs.existsSync(path.join(root, '.ultra', 'memory', 'memory.db')));
|
|
66
|
-
|
|
67
|
-
const result = legacy.pruneLegacy(root, {
|
|
68
|
-
confirm: 'DELETE_ULTRA_LEGACY_MEMORY',
|
|
69
|
-
timestamp: '2026-07-15T00-00-01-000Z',
|
|
70
|
-
});
|
|
71
|
-
assert.equal(result.pruned, true);
|
|
72
|
-
assert.ok(fs.existsSync(result.archive_dir));
|
|
73
|
-
assert.ok(!fs.existsSync(path.join(root, '.ultra', 'memory')));
|
|
74
|
-
const db = new Database(path.join(root, '.ultra', 'state.db'), { readonly: true });
|
|
75
|
-
assert.equal(db.prepare("SELECT 1 FROM sqlite_master WHERE name = 'memory_entries'").get(), undefined);
|
|
76
|
-
db.close();
|
|
77
|
-
} finally {
|
|
78
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
79
|
-
}
|
|
80
|
-
});
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Phase 6.3 — ultra-tools status cost panel:
|
|
4
|
-
// • `ultra-tools status --cost --json` prints {period, by_runtime,
|
|
5
|
-
// top_tasks, total_cost_usd}.
|
|
6
|
-
// • Default human-readable mode renders a summary table.
|
|
7
|
-
// • --since 7d filters telemetry to the last 7 days.
|
|
8
|
-
|
|
9
|
-
const { test } = require('node:test');
|
|
10
|
-
const assert = require('node:assert/strict');
|
|
11
|
-
const fs = require('node:fs');
|
|
12
|
-
const os = require('node:os');
|
|
13
|
-
const path = require('node:path');
|
|
14
|
-
|
|
15
|
-
const { initStateDb, closeStateDb } = require('../mcp-server/lib/state-db.cjs');
|
|
16
|
-
const ops = require('../mcp-server/lib/state-ops.cjs');
|
|
17
|
-
const telemetry = require('../mcp-server/lib/telemetry.cjs');
|
|
18
|
-
const statusCmd = require('./commands/status.cjs');
|
|
19
|
-
|
|
20
|
-
function freshFixture() {
|
|
21
|
-
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ubp-status-'));
|
|
22
|
-
const file = path.join(dir, '.ultra', 'state.db');
|
|
23
|
-
const init = initStateDb(file);
|
|
24
|
-
return { dir, db: init.db };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function teardown(dir, db) {
|
|
28
|
-
try { closeStateDb(db); } catch (_) { /* ignore */ }
|
|
29
|
-
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_) { /* ignore */ }
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function seedCalls(db, dir) {
|
|
33
|
-
ops.createTask(db, { id: 't-cheap', title: 'cheap', type: 'feature', priority: 'P2' });
|
|
34
|
-
ops.createTask(db, { id: 't-pricey', title: 'pricey', type: 'feature', priority: 'P1' });
|
|
35
|
-
ops.createSession(db, { sid: 's-x', task_id: 't-cheap', runtime: 'claude', worktree_path: `${dir}/w1`, artifact_dir: `${dir}/a1` });
|
|
36
|
-
ops.createSession(db, { sid: 's-y', task_id: 't-pricey', runtime: 'codex', worktree_path: `${dir}/w2`, artifact_dir: `${dir}/a2` });
|
|
37
|
-
telemetry.appendTelemetry(db, { event_type: 'token_usage', tool_name: 't', session_id: 's-x', runtime: 'claude', tokens_input: 500, tokens_output: 100, rootDir: dir, payload: { model: 'claude-haiku-4-5' } });
|
|
38
|
-
telemetry.appendTelemetry(db, { event_type: 'token_usage', tool_name: 't', session_id: 's-y', runtime: 'codex', tokens_input: 10000, tokens_output: 2000, rootDir: dir, payload: { model: 'gpt-5.4' } });
|
|
39
|
-
telemetry.appendTelemetry(db, { event_type: 'tool_call', tool_name: 'task.list', session_id: null, rootDir: dir });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
test('status --json --cost returns by_runtime + top_tasks + total_cost', () => {
|
|
43
|
-
const { dir, db } = freshFixture();
|
|
44
|
-
try {
|
|
45
|
-
seedCalls(db, dir);
|
|
46
|
-
const out = statusCmd.buildCostPanel(db, { since: null, limit: 10 });
|
|
47
|
-
assert.ok(Array.isArray(out.by_runtime));
|
|
48
|
-
assert.ok(Array.isArray(out.top_tasks));
|
|
49
|
-
assert.ok(typeof out.total_cost_usd === 'number');
|
|
50
|
-
const byR = Object.fromEntries(out.by_runtime.map((r) => [r.runtime, r]));
|
|
51
|
-
assert.equal(byR.claude.calls, 1);
|
|
52
|
-
assert.equal(byR.codex.calls, 1);
|
|
53
|
-
assert.equal(byR.unknown.calls, 1); // CLI call with null session
|
|
54
|
-
assert.equal(out.top_tasks[0].task_id, 't-pricey');
|
|
55
|
-
assert.ok(out.total_cost_usd > 0);
|
|
56
|
-
} finally { teardown(dir, db); }
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test('buildCostPanel: empty db → zero totals, empty arrays', () => {
|
|
60
|
-
const { dir, db } = freshFixture();
|
|
61
|
-
try {
|
|
62
|
-
const out = statusCmd.buildCostPanel(db, {});
|
|
63
|
-
assert.equal(out.total_cost_usd, 0);
|
|
64
|
-
assert.equal(out.by_runtime.length, 0);
|
|
65
|
-
assert.equal(out.top_tasks.length, 0);
|
|
66
|
-
assert.deepEqual(out.baseline, {
|
|
67
|
-
id: null, mode: null, status: 'missing', health: 'fail',
|
|
68
|
-
repository_revision: null, blockers: ['BASELINE_MISSING'], warnings: [],
|
|
69
|
-
});
|
|
70
|
-
} finally { teardown(dir, db); }
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('status exposes an in-progress brownfield baseline from authoritative state', () => {
|
|
74
|
-
const { dir, db } = freshFixture();
|
|
75
|
-
try {
|
|
76
|
-
db.prepare(
|
|
77
|
-
`INSERT INTO baselines (id, project_name, mode, status)
|
|
78
|
-
VALUES ('adoption', 'legacy', 'brownfield', 'adopting')`,
|
|
79
|
-
).run();
|
|
80
|
-
const out = statusCmd.buildCostPanel(db, { rootDir: dir });
|
|
81
|
-
assert.equal(out.baseline.id, 'adoption');
|
|
82
|
-
assert.equal(out.baseline.mode, 'brownfield');
|
|
83
|
-
assert.equal(out.baseline.status, 'adopting');
|
|
84
|
-
assert.deepEqual(out.baseline.blockers, ['BASELINE_NOT_READY:adopting']);
|
|
85
|
-
assert.match(statusCmd.renderHuman(out), /Baseline: brownfield\/adopting.*blocked/i);
|
|
86
|
-
} finally { teardown(dir, db); }
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('status reports a legacy schema as migration-required instead of throwing SQLite errors', () => {
|
|
90
|
-
const { dir, db } = freshFixture();
|
|
91
|
-
try {
|
|
92
|
-
db.exec('DROP TABLE baselines');
|
|
93
|
-
const out = statusCmd.buildCostPanel(db, { rootDir: dir });
|
|
94
|
-
assert.equal(out.baseline.status, 'migration_required');
|
|
95
|
-
assert.deepEqual(out.baseline.blockers, ['BASELINE_SCHEMA_MIGRATION_REQUIRED']);
|
|
96
|
-
assert.match(statusCmd.renderHuman(out), /migration_required/i);
|
|
97
|
-
} finally { teardown(dir, db); }
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test('status summarizes the authoritative baseline gap ledger', () => {
|
|
101
|
-
const { dir, db } = freshFixture();
|
|
102
|
-
try {
|
|
103
|
-
db.prepare(
|
|
104
|
-
`INSERT INTO baselines (id, project_name, mode, status, gaps_json)
|
|
105
|
-
VALUES ('adoption', 'legacy', 'brownfield', 'adopting', ?)`,
|
|
106
|
-
).run(JSON.stringify([
|
|
107
|
-
{ id: 'blocker', category: 'baseline_blocker', status: 'open', blocking: true, summary: 'Missing proof', evidence_refs: [] },
|
|
108
|
-
{ id: 'debt', category: 'technical_debt', status: 'accepted', blocking: false, summary: 'Slow test', evidence_refs: [] },
|
|
109
|
-
]));
|
|
110
|
-
const out = statusCmd.buildCostPanel(db, { rootDir: dir });
|
|
111
|
-
assert.deepEqual(out.baseline.gaps, {
|
|
112
|
-
total: 2, open: 1, blocking: 1,
|
|
113
|
-
by_category: { baseline_blocker: 1, technical_debt: 1 },
|
|
114
|
-
});
|
|
115
|
-
} finally { teardown(dir, db); }
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test('buildCostPanel: since cutoff in future → all filtered out', () => {
|
|
119
|
-
const { dir, db } = freshFixture();
|
|
120
|
-
try {
|
|
121
|
-
seedCalls(db, dir);
|
|
122
|
-
const future = new Date(Date.now() + 60 * 1000).toISOString();
|
|
123
|
-
const out = statusCmd.buildCostPanel(db, { since: future });
|
|
124
|
-
assert.equal(out.by_runtime.length, 0);
|
|
125
|
-
assert.equal(out.top_tasks.length, 0);
|
|
126
|
-
} finally { teardown(dir, db); }
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
test('parseSince: 7d / 24h / iso passthrough', () => {
|
|
130
|
-
const now = Date.parse('2026-04-17T12:00:00Z');
|
|
131
|
-
const sevenDaysAgo = statusCmd.parseSince('7d', now);
|
|
132
|
-
const expectedDayCutoff = new Date(now - 7 * 24 * 3600 * 1000).toISOString();
|
|
133
|
-
assert.equal(sevenDaysAgo, expectedDayCutoff);
|
|
134
|
-
assert.equal(statusCmd.parseSince('24h', now), new Date(now - 24 * 3600 * 1000).toISOString());
|
|
135
|
-
assert.equal(statusCmd.parseSince('2026-04-10T00:00:00Z'), '2026-04-10T00:00:00Z');
|
|
136
|
-
assert.equal(statusCmd.parseSince(null), null);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test('parseSince: invalid → throws', () => {
|
|
140
|
-
assert.throws(() => statusCmd.parseSince('bogus'), /since/i);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
test('renderHuman: includes key section headers', () => {
|
|
144
|
-
const { dir, db } = freshFixture();
|
|
145
|
-
try {
|
|
146
|
-
seedCalls(db, dir);
|
|
147
|
-
const panel = statusCmd.buildCostPanel(db, {});
|
|
148
|
-
const text = statusCmd.renderHuman(panel);
|
|
149
|
-
assert.match(text, /Cost by runtime/i);
|
|
150
|
-
assert.match(text, /Top tasks/i);
|
|
151
|
-
assert.match(text, /claude/);
|
|
152
|
-
assert.match(text, /codex/);
|
|
153
|
-
} finally { teardown(dir, db); }
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test('dispatch: --json echoes buildCostPanel output shape', () => {
|
|
157
|
-
const { dir, db } = freshFixture();
|
|
158
|
-
try {
|
|
159
|
-
seedCalls(db, dir);
|
|
160
|
-
closeStateDb(db);
|
|
161
|
-
const captured = [];
|
|
162
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
163
|
-
process.stdout.write = (chunk) => { captured.push(String(chunk)); return true; };
|
|
164
|
-
const envBackup = process.env.UBP_DB_PATH;
|
|
165
|
-
process.env.UBP_DB_PATH = path.join(dir, '.ultra', 'state.db');
|
|
166
|
-
try {
|
|
167
|
-
const code = statusCmd.dispatch(['--cost', '--json']);
|
|
168
|
-
assert.equal(code, 0);
|
|
169
|
-
} finally {
|
|
170
|
-
process.stdout.write = origWrite;
|
|
171
|
-
if (envBackup === undefined) delete process.env.UBP_DB_PATH; else process.env.UBP_DB_PATH = envBackup;
|
|
172
|
-
}
|
|
173
|
-
const joined = captured.join('');
|
|
174
|
-
const parsed = JSON.parse(joined);
|
|
175
|
-
assert.ok(parsed.ok);
|
|
176
|
-
assert.ok(parsed.data.by_runtime);
|
|
177
|
-
assert.ok(parsed.data.top_tasks);
|
|
178
|
-
} finally {
|
|
179
|
-
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_) { /* ignore */ }
|
|
180
|
-
}
|
|
181
|
-
});
|