ultra-builder-pro-cli 0.25.1 → 0.26.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/contexts/TEMPLATE.md +87 -0
- package/.ultra-template/decisions/.gitkeep +1 -0
- package/.ultra-template/evidence/.gitkeep +1 -0
- package/.ultra-template/north-star.md +46 -0
- package/.ultra-template/research/.gitkeep +1 -0
- package/.ultra-template/specs/discovery.md +2 -2
- package/.ultra-template/tasks.json +3 -0
- package/.ultra-template/test-report.json +27 -0
- package/CHANGELOG.md +40 -0
- package/README.md +217 -579
- package/adapters/_shared/delegate-profiles/kimi-read-only.md +15 -0
- package/adapters/_shared/delegate-profiles/kimi-write.md +17 -0
- package/adapters/_shared/file-ops.cjs +76 -2
- package/adapters/_shared/host-profile.cjs +97 -0
- package/adapters/_shared/plugin-core.cjs +204 -0
- package/adapters/_shared/runtime-assets.cjs +31 -96
- package/adapters/claude.js +92 -291
- package/adapters/codex.js +309 -842
- package/adapters/grok.js +143 -1469
- package/adapters/kimi.js +124 -1053
- package/adapters/opencode.js +230 -742
- package/bin/delegate-worker.cjs +323 -0
- package/bin/delegate.cjs +456 -0
- package/bin/install.js +38 -11
- package/docs/ARCHITECTURE.md +122 -216
- package/docs/ARTIFACT-AUTHORITY.md +90 -268
- package/docs/DECISIONS.md +75 -87
- package/docs/PHILOSOPHY.md +177 -0
- package/docs/PLUGIN-ISOLATION-CONTRACT.md +62 -39
- package/docs/RUNTIME-COMPAT-MATRIX.md +99 -156
- package/docs/SKILL-AUTHORING.md +94 -73
- package/docs/WORKFLOW-LIFECYCLE.md +93 -189
- package/hooks/README.md +17 -37
- package/hooks/_common.py +113 -0
- package/hooks/adapters/codex.py +9 -16
- package/hooks/adapters/grok.py +5 -5
- package/hooks/adapters/kimi.py +8 -14
- package/hooks/block_dangerous_commands.py +95 -0
- package/hooks/compact_context.py +65 -0
- package/hooks/mid_workflow_recall.py +25 -0
- package/hooks/post_edit_guard.py +105 -0
- package/hooks/session_context.py +30 -0
- package/package.json +17 -52
- package/skills/ultra-change/SKILL.md +97 -50
- package/skills/ultra-change/references/change-contract.md +40 -1
- package/skills/ultra-delegate/SKILL.md +82 -0
- package/skills/ultra-delegate/references/delegation-contract.md +51 -0
- package/skills/ultra-delegate/scripts/delegate_wait.py +86 -0
- package/skills/ultra-deliver/SKILL.md +61 -42
- package/skills/ultra-deliver/references/baseline-reconciliation.md +39 -20
- package/skills/ultra-dev/SKILL.md +96 -62
- package/skills/ultra-dev/references/debugging.md +22 -0
- package/skills/ultra-domain-modeling/SKILL.md +79 -0
- package/skills/ultra-grilling/SKILL.md +79 -0
- package/skills/ultra-init/SKILL.md +94 -37
- package/skills/ultra-init/references/brownfield-adoption.md +33 -83
- package/skills/ultra-init/scripts/init_project.cjs +84 -0
- package/skills/ultra-plan/SKILL.md +88 -68
- package/skills/ultra-research/SKILL.md +62 -48
- package/skills/ultra-research/references/00-problem-validation.md +5 -5
- package/skills/ultra-research/references/01-opportunity-discovery.md +3 -3
- package/skills/ultra-research/references/02-market-assessment.md +5 -4
- package/skills/ultra-research/references/03-competitive-landscape.md +3 -3
- package/skills/ultra-research/references/04-product-strategy.md +3 -3
- package/skills/ultra-research/references/05-assumptions-validation.md +5 -5
- package/skills/ultra-research/references/10-user-personas.md +3 -2
- package/skills/ultra-research/references/11-user-scenarios.md +3 -3
- package/skills/ultra-research/references/20-user-stories.md +4 -3
- package/skills/ultra-research/references/21-features-scope.md +3 -2
- package/skills/ultra-research/references/22-success-metrics.md +3 -3
- package/skills/ultra-research/references/30-architecture-context.md +3 -3
- package/skills/ultra-research/references/31-solution-strategy.md +3 -3
- package/skills/ultra-research/references/32-building-blocks.md +3 -3
- package/skills/ultra-research/references/40-deployment.md +3 -3
- package/skills/ultra-research/references/41-quality-risks.md +3 -3
- package/skills/ultra-research/references/99-synthesis.md +7 -7
- package/skills/ultra-review/SKILL.md +59 -32
- package/skills/ultra-review/references/code.md +17 -0
- package/skills/ultra-review/references/comments.md +12 -0
- package/skills/ultra-review/references/design.md +12 -0
- package/skills/ultra-review/references/errors.md +13 -0
- package/skills/ultra-review/references/spec.md +13 -0
- package/skills/ultra-review/references/tests.md +12 -0
- package/skills/ultra-review/references/worker-packet.md +40 -0
- package/skills/ultra-review/scripts/review_wait.py +26 -15
- package/skills/ultra-status/SKILL.md +61 -36
- package/skills/ultra-tdd/SKILL.md +81 -0
- package/skills/ultra-tdd/references/templates/README.md +20 -0
- package/skills/ultra-tdd/references/templates/feature-flag-default-audit.sh +79 -0
- package/skills/ultra-tdd/references/templates/persistence-real.ts +78 -0
- package/skills/ultra-tdd/references/templates/testcontainer-postgres.py +78 -0
- package/skills/ultra-tdd/references/templates/testcontainer-postgres.ts +73 -0
- package/skills/ultra-tdd/references/templates/vertical-slice.ts +66 -0
- package/skills/ultra-tdd/references/test-execution.md +18 -0
- package/skills/ultra-test/SKILL.md +70 -30
- package/skills/ultra-test/references/export-syntax.md +18 -0
- package/skills/ultra-test/scripts/worktree_digest.cjs +54 -0
- package/skills/ultra-think/SKILL.md +49 -27
- package/skills/ultra-think/references/autonomy-boundary.md +48 -0
- package/skills/ultra-think/references/interaction-boundary.md +18 -33
- package/.ultra-template/docs/research/README.md +0 -55
- package/.ultra-template/reports/templates/delivery-report.json +0 -17
- package/.ultra-template/reports/templates/test-report.json +0 -18
- package/.ultra-template/tasks/tasks.json +0 -15
- package/.ultra-template/templates/task-context.md +0 -19
- package/adapters/_shared/codex-assets.cjs +0 -725
- package/adapters/_shared/codex-ultra-tools-entry.cjs +0 -11
- package/adapters/_shared/interaction-contract.cjs +0 -129
- package/adapters/_shared/path-rewrite.cjs +0 -79
- package/adapters/_shared/runtime-native.cjs +0 -132
- package/adapters/_shared/settings-merge.cjs +0 -76
- package/agents/code-reviewer.md +0 -42
- package/agents/debugger.md +0 -40
- package/agents/review-code.md +0 -49
- package/agents/review-comments.md +0 -38
- package/agents/review-coordinator.md +0 -56
- package/agents/review-design.md +0 -40
- package/agents/review-errors.md +0 -43
- package/agents/review-spec.md +0 -39
- package/agents/review-tests.md +0 -40
- package/agents/tdd-runner.md +0 -38
- package/bin/orchestrator.js +0 -383
- package/commands/ultra-change.md +0 -10
- package/commands/ultra-deliver.md +0 -10
- package/commands/ultra-dev.md +0 -10
- package/commands/ultra-doctor.md +0 -10
- package/commands/ultra-init.md +0 -10
- package/commands/ultra-plan.md +0 -10
- package/commands/ultra-research.md +0 -10
- package/commands/ultra-review.md +0 -10
- package/commands/ultra-status.md +0 -10
- package/commands/ultra-test.md +0 -10
- package/commands/ultra-think.md +0 -10
- package/docs/AGENT-CONTEXT.md +0 -191
- package/docs/COMMIT-HASH-BACKFILL.md +0 -51
- package/docs/LEGACY-CLI-CROSSWALK.md +0 -175
- package/docs/ROADMAP.md +0 -101
- package/docs/STATE-DB-ACCESS-POLICY.md +0 -161
- package/hooks/active_task_context.py +0 -152
- package/hooks/context_envelope.py +0 -100
- package/hooks/health_check.py +0 -196
- package/hooks/pre_stop_check.py +0 -66
- package/hooks/runtime_paths.py +0 -396
- package/hooks/subagent_tracker.py +0 -88
- package/hooks/workflow_checkpoint.py +0 -76
- package/hooks/workflow_context.py +0 -48
- package/hooks/workflow_resume.py +0 -84
- package/mcp-server/hook-context.cjs +0 -38
- package/mcp-server/hook-event.cjs +0 -34
- package/mcp-server/lib/archive-journal.cjs +0 -1119
- package/mcp-server/lib/archive-mutation-worker.py +0 -365
- package/mcp-server/lib/artifact-registry.cjs +0 -1232
- package/mcp-server/lib/baseline-workflow.cjs +0 -1193
- package/mcp-server/lib/canonical-json.cjs +0 -19
- package/mcp-server/lib/change-authority.cjs +0 -26
- package/mcp-server/lib/change-packet.cjs +0 -790
- package/mcp-server/lib/change-workflow.cjs +0 -65
- package/mcp-server/lib/context-envelope.cjs +0 -581
- package/mcp-server/lib/context-paths.cjs +0 -142
- package/mcp-server/lib/decision-records.cjs +0 -423
- package/mcp-server/lib/delivery-transaction.cjs +0 -471
- package/mcp-server/lib/doctor-backup-worker.cjs +0 -328
- package/mcp-server/lib/doctor.cjs +0 -627
- package/mcp-server/lib/git-bootstrap.cjs +0 -651
- package/mcp-server/lib/init-project.cjs +0 -1021
- package/mcp-server/lib/kernel-change-workflow.cjs +0 -2029
- package/mcp-server/lib/legacy-change-workflow.cjs +0 -3032
- package/mcp-server/lib/managed-file-write.cjs +0 -172
- package/mcp-server/lib/plan-checkpoint.cjs +0 -177
- package/mcp-server/lib/plan-publication-worker.cjs +0 -830
- package/mcp-server/lib/plan-store.cjs +0 -794
- package/mcp-server/lib/prd-parser.cjs +0 -83
- package/mcp-server/lib/pricing.cjs +0 -44
- package/mcp-server/lib/project-context.cjs +0 -178
- package/mcp-server/lib/projector.cjs +0 -386
- package/mcp-server/lib/provider-refs.cjs +0 -42
- package/mcp-server/lib/runtime-paths.cjs +0 -1714
- package/mcp-server/lib/runtime-state.cjs +0 -222
- package/mcp-server/lib/safe-project-file.cjs +0 -385
- package/mcp-server/lib/stage-checkpoints.cjs +0 -463
- package/mcp-server/lib/state-authority.cjs +0 -95
- package/mcp-server/lib/state-db.cjs +0 -1580
- package/mcp-server/lib/state-ops.cjs +0 -1428
- package/mcp-server/lib/task-contract.cjs +0 -404
- package/mcp-server/lib/task-expander.cjs +0 -105
- package/mcp-server/lib/task-ledger.cjs +0 -1964
- package/mcp-server/lib/telemetry.cjs +0 -85
- package/mcp-server/lib/topo.cjs +0 -131
- package/mcp-server/lib/ultra-facade.cjs +0 -2684
- package/mcp-server/lib/worker-packet.cjs +0 -358
- package/mcp-server/server.cjs +0 -437
- package/orchestrator/auto-merge.cjs +0 -128
- package/orchestrator/daemon.cjs +0 -264
- package/orchestrator/dispatch-rules.cjs +0 -78
- package/orchestrator/parallel-orchestrator.cjs +0 -903
- package/orchestrator/planner/plan-builder.cjs +0 -125
- package/orchestrator/recovery.cjs +0 -188
- package/orchestrator/session-close-journal-worker.cjs +0 -5
- package/orchestrator/session-close-journal.cjs +0 -1014
- package/orchestrator/session-runner.cjs +0 -1153
- package/orchestrator/worktree-manager.cjs +0 -282
- package/settings.json +0 -7
- package/skills/cc-collab/SKILL.md +0 -42
- package/skills/code-review-expert/SKILL.md +0 -43
- package/skills/code-review-expert/references/correctness-reliability.md +0 -30
- package/skills/code-review-expert/references/design-boundaries.md +0 -26
- package/skills/code-review-expert/references/removal-plan.md +0 -24
- package/skills/codex-collab/SKILL.md +0 -49
- package/skills/codex-collab/evals/evals.json +0 -35
- package/skills/integration-rules/SKILL.md +0 -32
- package/skills/security-rules/SKILL.md +0 -38
- package/skills/testing-rules/SKILL.md +0 -32
- package/skills/ultra-doctor/SKILL.md +0 -41
- package/skills/ultra-plan/references/semantic-preflight.md +0 -20
- package/skills/ultra-verify/SKILL.md +0 -46
- package/skills/ultra-verify/evals/evals.json +0 -46
- package/skills/ultra-verify/references/cross-verify-modes.md +0 -28
- package/skills/ultra-verify/references/evidence-status.md +0 -21
- package/skills/ultra-verify/scripts/verify_wait.py +0 -94
- package/spec/cli-protocol.md +0 -102
- package/spec/mcp-tools.yaml +0 -312
- package/spec/schemas/baseline-reconciliation.v1.schema.json +0 -73
- package/spec/schemas/change-delta.v1.schema.json +0 -153
- package/spec/schemas/command-manifest.schema.json +0 -29
- package/spec/schemas/context-file.v4.5.schema.json +0 -54
- package/spec/schemas/delivery-report.v1.schema.json +0 -66
- package/spec/schemas/documentation-reconciliation.v1.schema.json +0 -106
- package/spec/schemas/mcp-tools.schema.json +0 -136
- package/spec/schemas/skill-manifest.schema.json +0 -24
- package/spec/schemas/state-db.sql +0 -641
- package/spec/schemas/task-ledger.v2.schema.json +0 -296
- package/spec/schemas/tasks.v4.5.schema.json +0 -96
- package/spec/schemas/test-report.v1.schema.json +0 -225
- package/ultra-tools/cli.cjs +0 -129
- package/ultra-tools/commands/db.cjs +0 -226
- package/ultra-tools/commands/legacy-memory.cjs +0 -159
- package/ultra-tools/commands/migrate.cjs +0 -1386
- package/ultra-tools/commands/session.cjs +0 -213
- package/ultra-tools/commands/status.cjs +0 -559
- package/ultra-tools/commands/system.cjs +0 -498
- package/ultra-tools/commands/task.cjs +0 -105
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Phase 8A.4a — Execution plan builder (Functional Core, no IO).
|
|
4
|
-
//
|
|
5
|
-
// buildPlan(tasks) → { waves, ownership_forecast, conflict_surface,
|
|
6
|
-
// estimated_cost_usd, estimated_duration_min, cycles }.
|
|
7
|
-
// Composes topo.computeWaves + pricing.computeCost with a file-overlap
|
|
8
|
-
// conflict detector. Downstream 8A.4b writes this object to
|
|
9
|
-
// the owning Change's deterministic plan.json and plan.md artifacts.
|
|
10
|
-
|
|
11
|
-
const { computeWaves } = require('../../mcp-server/lib/topo.cjs');
|
|
12
|
-
const { computeCost } = require('../../mcp-server/lib/pricing.cjs');
|
|
13
|
-
|
|
14
|
-
const DEFAULT_COMPLEXITY = 3;
|
|
15
|
-
const TOKENS_PER_COMPLEXITY = Object.freeze({ input: 5000, output: 2000 });
|
|
16
|
-
const DURATION_MIN_PER_COMPLEXITY = 5;
|
|
17
|
-
|
|
18
|
-
function buildPlan(tasks, { runtime = null, model = null, changeId = null } = {}) {
|
|
19
|
-
if (!Array.isArray(tasks)) {
|
|
20
|
-
throw new TypeError('buildPlan: tasks must be an array');
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Plan artifacts are durable and compared by digest. Keep their ordering
|
|
24
|
-
// independent from SQLite insertion order or caller enumeration order.
|
|
25
|
-
const orderedTasks = [...tasks].sort((left, right) => String(left.id).localeCompare(String(right.id)));
|
|
26
|
-
|
|
27
|
-
const graph = orderedTasks.map((t) => ({
|
|
28
|
-
id: t.id,
|
|
29
|
-
deps: Array.isArray(t.deps) ? t.deps : [],
|
|
30
|
-
}));
|
|
31
|
-
const { waves, cycles } = computeWaves(graph);
|
|
32
|
-
|
|
33
|
-
const byId = new Map(orderedTasks.map((t) => [t.id, t]));
|
|
34
|
-
|
|
35
|
-
const conflict_surface = [];
|
|
36
|
-
const wavePayload = waves.map((waveTasks, idx) => {
|
|
37
|
-
const pairs = listWaveConflicts(waveTasks, byId);
|
|
38
|
-
for (const p of pairs) {
|
|
39
|
-
conflict_surface.push({
|
|
40
|
-
files: p.files,
|
|
41
|
-
tasks: p.tasks,
|
|
42
|
-
recommend: 'sequentialize',
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
const hasConflict = pairs.length > 0;
|
|
46
|
-
const entry = {
|
|
47
|
-
id: idx + 1,
|
|
48
|
-
tasks: waveTasks,
|
|
49
|
-
parallel: !hasConflict && waveTasks.length > 1,
|
|
50
|
-
};
|
|
51
|
-
if (hasConflict) {
|
|
52
|
-
const sharedFiles = [...new Set(pairs.flatMap((p) => p.files))];
|
|
53
|
-
entry.reason = `shared files: ${sharedFiles.join(', ')}`;
|
|
54
|
-
}
|
|
55
|
-
return entry;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const ownership_forecast = {};
|
|
59
|
-
for (const t of orderedTasks) {
|
|
60
|
-
ownership_forecast[t.id] = Array.isArray(t.files_modified) ? t.files_modified : [];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const taskCosts = orderedTasks.map((t) => {
|
|
64
|
-
const complexity = Number(t.complexity || DEFAULT_COMPLEXITY);
|
|
65
|
-
const ti = complexity * TOKENS_PER_COMPLEXITY.input;
|
|
66
|
-
const to = complexity * TOKENS_PER_COMPLEXITY.output;
|
|
67
|
-
return computeCost(runtime, model, ti, to);
|
|
68
|
-
});
|
|
69
|
-
const estimated_cost_usd = orderedTasks.length === 0
|
|
70
|
-
? 0
|
|
71
|
-
: (taskCosts.some((cost) => cost === null)
|
|
72
|
-
? null
|
|
73
|
-
: round4(taskCosts.reduce((sum, cost) => sum + cost, 0)));
|
|
74
|
-
|
|
75
|
-
const estimated_duration_min = wavePayload.reduce((acc, wv) => {
|
|
76
|
-
const complexities = wv.tasks.map(
|
|
77
|
-
(id) => Number(byId.get(id)?.complexity || DEFAULT_COMPLEXITY),
|
|
78
|
-
);
|
|
79
|
-
if (wv.parallel) {
|
|
80
|
-
return acc + Math.max(0, ...complexities) * DURATION_MIN_PER_COMPLEXITY;
|
|
81
|
-
}
|
|
82
|
-
return acc + complexities.reduce((a, b) => a + b, 0) * DURATION_MIN_PER_COMPLEXITY;
|
|
83
|
-
}, 0);
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
schema_version: '1.0',
|
|
87
|
-
change_id: changeId,
|
|
88
|
-
waves: wavePayload,
|
|
89
|
-
ownership_forecast,
|
|
90
|
-
conflict_surface,
|
|
91
|
-
estimated_cost_usd,
|
|
92
|
-
estimated_duration_min,
|
|
93
|
-
cycles,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function listWaveConflicts(waveTasks, byId) {
|
|
98
|
-
const conflicts = [];
|
|
99
|
-
for (let i = 0; i < waveTasks.length; i++) {
|
|
100
|
-
for (let j = i + 1; j < waveTasks.length; j++) {
|
|
101
|
-
const a = waveTasks[i];
|
|
102
|
-
const b = waveTasks[j];
|
|
103
|
-
const aFiles = new Set(byId.get(a)?.files_modified || []);
|
|
104
|
-
const bFiles = new Set(byId.get(b)?.files_modified || []);
|
|
105
|
-
const overlap = [];
|
|
106
|
-
for (const f of aFiles) if (bFiles.has(f)) overlap.push(f);
|
|
107
|
-
if (overlap.length > 0) {
|
|
108
|
-
conflicts.push({ files: overlap, tasks: [a, b] });
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return conflicts;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function round4(n) {
|
|
116
|
-
return Math.round(n * 10000) / 10000;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
module.exports = {
|
|
120
|
-
buildPlan,
|
|
121
|
-
listWaveConflicts,
|
|
122
|
-
DEFAULT_COMPLEXITY,
|
|
123
|
-
TOKENS_PER_COMPLEXITY,
|
|
124
|
-
DURATION_MIN_PER_COMPLEXITY,
|
|
125
|
-
};
|
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Phase 5.1 — Recovery decision layer.
|
|
4
|
-
//
|
|
5
|
-
// Phase 4.5's reapOrphanSessions marks running sessions whose lease or
|
|
6
|
-
// heartbeat has expired as `orphan`. Orphan is a safe waypoint — we don't yet
|
|
7
|
-
// know whether the child is dead, deadlocked, or just slow.
|
|
8
|
-
//
|
|
9
|
-
// recoverOnBoot upgrades orphans whose pid is demonstrably dead to `crashed`,
|
|
10
|
-
// records a task_failure event, and leaves orphans with live pids alone so
|
|
11
|
-
// they can recover on their own heartbeat.
|
|
12
|
-
//
|
|
13
|
-
// The circuit breaker (Phase 5.2) consumes task_failure events to decide
|
|
14
|
-
// whether the task should be tripped.
|
|
15
|
-
|
|
16
|
-
const ops = require('../mcp-server/lib/state-ops.cjs');
|
|
17
|
-
const fs = require('node:fs');
|
|
18
|
-
const path = require('node:path');
|
|
19
|
-
const closeJournal = require('./session-close-journal.cjs');
|
|
20
|
-
const runner = require('./session-runner.cjs');
|
|
21
|
-
|
|
22
|
-
function isPidAlive(pid) {
|
|
23
|
-
if (!pid || pid <= 0) return false;
|
|
24
|
-
try {
|
|
25
|
-
// Signal 0 performs error checking without actually sending a signal.
|
|
26
|
-
process.kill(pid, 0);
|
|
27
|
-
return true;
|
|
28
|
-
} catch (err) {
|
|
29
|
-
// ESRCH = no such process; EPERM = process exists but owned by another user
|
|
30
|
-
// (still counts as alive for our purposes).
|
|
31
|
-
if (err && err.code === 'EPERM') return true;
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function listOrphanSessions(db) {
|
|
37
|
-
return db.prepare("SELECT * FROM sessions WHERE status = 'orphan'").all();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function reconcileCloseWorktree(repoRoot, intent) {
|
|
41
|
-
runner._internal.assertManagedWorktreePath(
|
|
42
|
-
repoRoot,
|
|
43
|
-
intent.worktree_path,
|
|
44
|
-
{ sid: intent.sid, validateRuntimeTree: false },
|
|
45
|
-
);
|
|
46
|
-
if (fs.existsSync(intent.worktree_path)) {
|
|
47
|
-
const blocker = runner._internal.worktreeRemovalBlocker(
|
|
48
|
-
repoRoot,
|
|
49
|
-
intent.worktree_path,
|
|
50
|
-
);
|
|
51
|
-
if (blocker) throw new Error(blocker);
|
|
52
|
-
}
|
|
53
|
-
runner._internal.reconcileRemovedWorktree(
|
|
54
|
-
repoRoot,
|
|
55
|
-
intent.worktree_path,
|
|
56
|
-
{ sid: intent.sid },
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function closeIntentMatchesSession(intent, session) {
|
|
61
|
-
return Boolean(
|
|
62
|
-
intent
|
|
63
|
-
&& session
|
|
64
|
-
&& intent.sid === session.sid
|
|
65
|
-
&& intent.task_id === session.task_id
|
|
66
|
-
&& typeof intent.worktree_path === 'string'
|
|
67
|
-
&& typeof session.worktree_path === 'string'
|
|
68
|
-
&& path.resolve(intent.worktree_path) === path.resolve(session.worktree_path),
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function recoverCloseIntents(db, repoRoot) {
|
|
73
|
-
if (!repoRoot) return { recovered: [], pending: [] };
|
|
74
|
-
const recovered = [];
|
|
75
|
-
const pending = [];
|
|
76
|
-
for (const intent of closeJournal.list(repoRoot)) {
|
|
77
|
-
const session = ops.readSession(db, intent.sid);
|
|
78
|
-
if (!session) {
|
|
79
|
-
pending.push({ sid: intent.sid, reason: 'session_missing' });
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (!closeIntentMatchesSession(intent, session)) {
|
|
83
|
-
pending.push({ sid: intent.sid, reason: 'journal_identity_mismatch' });
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (session.status === intent.requested_status) {
|
|
87
|
-
try {
|
|
88
|
-
reconcileCloseWorktree(repoRoot, intent);
|
|
89
|
-
closeJournal.discard(repoRoot, intent.sid);
|
|
90
|
-
recovered.push({
|
|
91
|
-
sid: intent.sid,
|
|
92
|
-
status: session.status,
|
|
93
|
-
reconciled: 'already_terminal',
|
|
94
|
-
});
|
|
95
|
-
} catch (error) {
|
|
96
|
-
closeJournal.update(repoRoot, intent.sid, {
|
|
97
|
-
phase: 'recovery_failed',
|
|
98
|
-
error: error.message,
|
|
99
|
-
});
|
|
100
|
-
pending.push({ sid: intent.sid, reason: error.message });
|
|
101
|
-
}
|
|
102
|
-
continue;
|
|
103
|
-
}
|
|
104
|
-
if (['completed', 'crashed'].includes(session.status)) {
|
|
105
|
-
pending.push({
|
|
106
|
-
sid: intent.sid,
|
|
107
|
-
reason: `terminal_status_conflict:${session.status}`,
|
|
108
|
-
});
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
if (isPidAlive(session.pid)) {
|
|
112
|
-
pending.push({ sid: intent.sid, reason: 'worker_still_alive' });
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
try {
|
|
116
|
-
reconcileCloseWorktree(repoRoot, intent);
|
|
117
|
-
closeJournal.update(repoRoot, intent.sid, {
|
|
118
|
-
phase: 'worktree_removed',
|
|
119
|
-
error: null,
|
|
120
|
-
});
|
|
121
|
-
ops.updateSession(db, intent.sid, { status: intent.requested_status });
|
|
122
|
-
closeJournal.discard(repoRoot, intent.sid);
|
|
123
|
-
recovered.push({
|
|
124
|
-
sid: intent.sid,
|
|
125
|
-
status: intent.requested_status,
|
|
126
|
-
reconciled: 'close_intent',
|
|
127
|
-
});
|
|
128
|
-
} catch (error) {
|
|
129
|
-
closeJournal.update(repoRoot, intent.sid, {
|
|
130
|
-
phase: 'recovery_failed',
|
|
131
|
-
error: error.message,
|
|
132
|
-
});
|
|
133
|
-
pending.push({ sid: intent.sid, reason: error.message });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return { recovered, pending };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function recoverOnBoot(db, { graceSeconds = 300, repoRoot = null } = {}) {
|
|
140
|
-
if (!db) throw new Error('recoverOnBoot: db handle required');
|
|
141
|
-
|
|
142
|
-
const closeRecovery = recoverCloseIntents(db, repoRoot);
|
|
143
|
-
|
|
144
|
-
// Step 1 — promote stale running sessions to orphan (Phase 4.5 logic).
|
|
145
|
-
ops.reapOrphanSessions(db, {
|
|
146
|
-
graceSeconds,
|
|
147
|
-
exclude_session_ids: closeRecovery.pending.map((entry) => entry.sid),
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Step 2 — for every orphan, decide crashed vs. keep-watching.
|
|
151
|
-
const orphans = listOrphanSessions(db);
|
|
152
|
-
const recovered = [];
|
|
153
|
-
for (const s of orphans) {
|
|
154
|
-
const alive = isPidAlive(s.pid);
|
|
155
|
-
if (alive) continue;
|
|
156
|
-
|
|
157
|
-
const resolution = ops.crashOrphanSession(db, s.sid, {
|
|
158
|
-
reason: 'session_crashed_on_boot',
|
|
159
|
-
});
|
|
160
|
-
if (!resolution.changed) continue;
|
|
161
|
-
recovered.push({
|
|
162
|
-
sid: s.sid,
|
|
163
|
-
task_id: s.task_id,
|
|
164
|
-
pid: s.pid,
|
|
165
|
-
pid_alive: false,
|
|
166
|
-
reason: 'session_crashed_on_boot',
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return {
|
|
171
|
-
recovered,
|
|
172
|
-
count: recovered.length,
|
|
173
|
-
closed: closeRecovery.recovered,
|
|
174
|
-
close_pending: closeRecovery.pending,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
module.exports = {
|
|
179
|
-
recoverOnBoot,
|
|
180
|
-
// exposed for tests
|
|
181
|
-
_internal: {
|
|
182
|
-
isPidAlive,
|
|
183
|
-
listOrphanSessions,
|
|
184
|
-
closeIntentMatchesSession,
|
|
185
|
-
reconcileCloseWorktree,
|
|
186
|
-
recoverCloseIntents,
|
|
187
|
-
},
|
|
188
|
-
};
|