instar 0.28.82 → 0.28.84
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/dashboard/index.html +239 -0
- package/dist/cli.js +10 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/memoryBackfillEvidence.d.ts +69 -0
- package/dist/commands/memoryBackfillEvidence.d.ts.map +1 -0
- package/dist/commands/memoryBackfillEvidence.js +220 -0
- package/dist/commands/memoryBackfillEvidence.js.map +1 -0
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +72 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/EvolutionManager.d.ts +73 -0
- package/dist/core/EvolutionManager.d.ts.map +1 -1
- package/dist/core/EvolutionManager.js +376 -0
- package/dist/core/EvolutionManager.js.map +1 -1
- package/dist/core/SharedStateLedger.d.ts.map +1 -1
- package/dist/core/SharedStateLedger.js +4 -0
- package/dist/core/SharedStateLedger.js.map +1 -1
- package/dist/core/types.d.ts +90 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +8 -0
- package/dist/core/types.js.map +1 -1
- package/dist/memory/EvidenceRenderer.d.ts +64 -0
- package/dist/memory/EvidenceRenderer.d.ts.map +1 -0
- package/dist/memory/EvidenceRenderer.js +119 -0
- package/dist/memory/EvidenceRenderer.js.map +1 -0
- package/dist/memory/SemanticMemory.d.ts +61 -1
- package/dist/memory/SemanticMemory.d.ts.map +1 -1
- package/dist/memory/SemanticMemory.js +381 -11
- package/dist/memory/SemanticMemory.js.map +1 -1
- package/dist/server/AgentServer.d.ts +4 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +2 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts +8 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +493 -3
- package/dist/server/routes.js.map +1 -1
- package/dist/tasks/DivergenceChecker.d.ts +89 -0
- package/dist/tasks/DivergenceChecker.d.ts.map +1 -0
- package/dist/tasks/DivergenceChecker.js +255 -0
- package/dist/tasks/DivergenceChecker.js.map +1 -0
- package/dist/tasks/LruCache.d.ts +33 -0
- package/dist/tasks/LruCache.d.ts.map +1 -0
- package/dist/tasks/LruCache.js +88 -0
- package/dist/tasks/LruCache.js.map +1 -0
- package/dist/tasks/RateLimiter.d.ts +61 -0
- package/dist/tasks/RateLimiter.d.ts.map +1 -0
- package/dist/tasks/RateLimiter.js +108 -0
- package/dist/tasks/RateLimiter.js.map +1 -0
- package/dist/tasks/TaskFlowDueWaker.d.ts +29 -0
- package/dist/tasks/TaskFlowDueWaker.d.ts.map +1 -0
- package/dist/tasks/TaskFlowDueWaker.js +73 -0
- package/dist/tasks/TaskFlowDueWaker.js.map +1 -0
- package/dist/tasks/TaskFlowMaintenanceSweeper.d.ts +40 -0
- package/dist/tasks/TaskFlowMaintenanceSweeper.d.ts.map +1 -0
- package/dist/tasks/TaskFlowMaintenanceSweeper.js +131 -0
- package/dist/tasks/TaskFlowMaintenanceSweeper.js.map +1 -0
- package/dist/tasks/TaskFlowRegistry.d.ts +180 -0
- package/dist/tasks/TaskFlowRegistry.d.ts.map +1 -0
- package/dist/tasks/TaskFlowRegistry.js +791 -0
- package/dist/tasks/TaskFlowRegistry.js.map +1 -0
- package/dist/tasks/ThreadlineFlowBridge.d.ts +54 -0
- package/dist/tasks/ThreadlineFlowBridge.d.ts.map +1 -0
- package/dist/tasks/ThreadlineFlowBridge.js +129 -0
- package/dist/tasks/ThreadlineFlowBridge.js.map +1 -0
- package/dist/tasks/task-flow-registry.store.sqlite.d.ts +77 -0
- package/dist/tasks/task-flow-registry.store.sqlite.d.ts.map +1 -0
- package/dist/tasks/task-flow-registry.store.sqlite.js +350 -0
- package/dist/tasks/task-flow-registry.store.sqlite.js.map +1 -0
- package/dist/tasks/task-flow-types.d.ts +229 -0
- package/dist/tasks/task-flow-types.d.ts.map +1 -0
- package/dist/tasks/task-flow-types.js +128 -0
- package/dist/tasks/task-flow-types.js.map +1 -0
- package/dist/threadline/ThreadlineNicknames.d.ts +88 -0
- package/dist/threadline/ThreadlineNicknames.d.ts.map +1 -0
- package/dist/threadline/ThreadlineNicknames.js +195 -0
- package/dist/threadline/ThreadlineNicknames.js.map +1 -0
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +78 -78
- package/upgrades/0.28.83.md +45 -0
- package/upgrades/0.28.84.md +89 -0
- package/upgrades/side-effects/taskflow-phase1.md +130 -0
- package/upgrades/side-effects/taskflow-phase2.md +110 -0
- package/upgrades/side-effects/taskflow-phase3a.md +138 -0
- package/upgrades/side-effects/taskflow-phase5.md +234 -0
- package/upgrades/side-effects/threadline-nickname-resolver-authority.md +81 -0
- package/upgrades/side-effects/wikiclaim-evidence-phase1.md +138 -0
- package/upgrades/side-effects/wikiclaim-evidence-phase4.md +129 -0
- package/upgrades/side-effects/wikiclaim-evidence-phase5.md +285 -0
|
@@ -18,6 +18,7 @@ import type { EvolutionProposal, EvolutionType, EvolutionStatus, LearningEntry,
|
|
|
18
18
|
import type { TrustElevationTracker } from './TrustElevationTracker.js';
|
|
19
19
|
import type { AutonomousEvolution, ReviewResult } from './AutonomousEvolution.js';
|
|
20
20
|
import type { AutonomyProfileManager } from './AutonomyProfileManager.js';
|
|
21
|
+
import type { TaskFlowRegistry } from '../tasks/TaskFlowRegistry.js';
|
|
21
22
|
interface EvolutionState {
|
|
22
23
|
proposals: EvolutionProposal[];
|
|
23
24
|
stats: {
|
|
@@ -63,7 +64,79 @@ export declare class EvolutionManager {
|
|
|
63
64
|
private trustElevationTracker;
|
|
64
65
|
private autonomousEvolution;
|
|
65
66
|
private autonomyManager;
|
|
67
|
+
/** Registry handle (set when TaskFlow is enabled). */
|
|
68
|
+
private taskFlowRegistry;
|
|
69
|
+
/** Per-process controller-instance id (server uuid). */
|
|
70
|
+
private taskFlowControllerInstanceId;
|
|
71
|
+
/**
|
|
72
|
+
* Set to true by DivergenceChecker when state-divergence is detected.
|
|
73
|
+
* When true, EvolutionManager continues JSON-only writes and DOES NOT
|
|
74
|
+
* dual-write to TaskFlow. The signal is reset on next divergence pass that
|
|
75
|
+
* reports zero divergence. This is a signal-emitted brake, not a hard gate.
|
|
76
|
+
*/
|
|
77
|
+
private taskFlowShadowWritesHalted;
|
|
78
|
+
/** Most recent reason for halting; surfaced in /health and tests. */
|
|
79
|
+
private taskFlowShadowWritesHaltReason;
|
|
80
|
+
/**
|
|
81
|
+
* Source of the most recent halt. DivergenceChecker uses this to ensure it
|
|
82
|
+
* only auto-clears halts that DivergenceChecker itself imposed — an operator
|
|
83
|
+
* or sibling system's manual halt stays in place until that source clears it.
|
|
84
|
+
*/
|
|
85
|
+
private taskFlowShadowWritesHaltSource;
|
|
66
86
|
constructor(config: EvolutionManagerConfig);
|
|
87
|
+
/**
|
|
88
|
+
* Wire a TaskFlow registry for Phase 3a dual-write. After this call:
|
|
89
|
+
* - addProposal / updateProposalStatus shadow-write to TaskFlow
|
|
90
|
+
* - migrateExistingToTaskFlow() backfills any in-flight clusters
|
|
91
|
+
* - DivergenceChecker can call setShadowWritesHalted() to brake writes
|
|
92
|
+
*
|
|
93
|
+
* Idempotent — safe to call multiple times; subsequent calls overwrite.
|
|
94
|
+
*/
|
|
95
|
+
setTaskFlowRegistry(registry: TaskFlowRegistry, controllerInstanceId: string): void;
|
|
96
|
+
getTaskFlowRegistry(): TaskFlowRegistry | null;
|
|
97
|
+
/**
|
|
98
|
+
* Halt or resume TaskFlow shadow-writes. Called by DivergenceChecker on a
|
|
99
|
+
* mismatch. Signal-vs-authority compliance: this is a signal-consumed brake,
|
|
100
|
+
* not a brittle blocker — the divergence checker decides, EvolutionManager
|
|
101
|
+
* obeys. JSON writes continue regardless (read-authoritative TaskFlow is
|
|
102
|
+
* shadow-mode in Phase 3a).
|
|
103
|
+
*
|
|
104
|
+
* The optional `source` parameter tags the halt with its origin so an
|
|
105
|
+
* automatic clearer (DivergenceChecker) only cancels halts it caused.
|
|
106
|
+
* Defaults to `'manual'` when omitted.
|
|
107
|
+
*/
|
|
108
|
+
setShadowWritesHalted(halted: boolean, reason?: string, source?: string): void;
|
|
109
|
+
isShadowWritesHalted(): {
|
|
110
|
+
halted: boolean;
|
|
111
|
+
reason: string | null;
|
|
112
|
+
source: string | null;
|
|
113
|
+
};
|
|
114
|
+
private taskFlowPrincipal;
|
|
115
|
+
/**
|
|
116
|
+
* Best-effort dual-write to TaskFlow. NEVER throws to the caller — JSON
|
|
117
|
+
* remains the local durability path; TaskFlow is shadow until Phase 3b
|
|
118
|
+
* cutover. All TaskFlow errors land in console and the divergence checker
|
|
119
|
+
* picks up via state comparison.
|
|
120
|
+
*/
|
|
121
|
+
private dualWriteCreate;
|
|
122
|
+
private dualWriteTransition;
|
|
123
|
+
private logTaskFlowError;
|
|
124
|
+
/**
|
|
125
|
+
* Backfill all in-flight proposals (status !== 'implemented' && !== 'rejected')
|
|
126
|
+
* into TaskFlow. Idempotent via `findIdempotent` on
|
|
127
|
+
* `evolution:cluster:<proposalId>` ownerKey. Safe to call multiple times.
|
|
128
|
+
*
|
|
129
|
+
* Returns counts: created, alreadyExisted, advanced (transitioned beyond
|
|
130
|
+
* 'queued' to match the proposal's current status).
|
|
131
|
+
*/
|
|
132
|
+
migrateExistingToTaskFlow(): Promise<{
|
|
133
|
+
created: number;
|
|
134
|
+
alreadyExisted: number;
|
|
135
|
+
advanced: number;
|
|
136
|
+
skipped: number;
|
|
137
|
+
}>;
|
|
138
|
+
/** Catch a flow record up to the proposal's current status. */
|
|
139
|
+
private catchUpFlowToStatus;
|
|
67
140
|
/**
|
|
68
141
|
* Wire adaptive autonomy modules for runtime integration.
|
|
69
142
|
* - TrustElevationTracker: receives proposal approval/rejection events
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EvolutionManager.d.ts","sourceRoot":"","sources":["../../src/core/EvolutionManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EACV,iBAAiB,EACjB,aAAa,EACb,eAAe,EACf,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,EACX,UAAU,EACV,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAClF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"EvolutionManager.d.ts","sourceRoot":"","sources":["../../src/core/EvolutionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,16 +17,386 @@
|
|
|
17
17
|
import fs from 'node:fs';
|
|
18
18
|
import path from 'node:path';
|
|
19
19
|
import { SafeFsExecutor } from './SafeFsExecutor.js';
|
|
20
|
+
import { TaskFlowError } from '../tasks/task-flow-types.js';
|
|
21
|
+
/**
|
|
22
|
+
* TaskFlow controller identity for EvolutionManager (Phase 3a dual-write).
|
|
23
|
+
* Every proposal's flow is owned by this controllerId; the registry uses it
|
|
24
|
+
* for OCC scope checks. Single-instance per server process.
|
|
25
|
+
*/
|
|
26
|
+
const EVOLUTION_TASKFLOW_CONTROLLER_ID = 'EvolutionManager';
|
|
27
|
+
function statusToFlowAction(prev, next, resolution) {
|
|
28
|
+
// Initial create-from-proposed is handled in addProposal directly.
|
|
29
|
+
if (prev === null && next === 'proposed')
|
|
30
|
+
return { kind: 'create' };
|
|
31
|
+
if (prev === next)
|
|
32
|
+
return { kind: 'noop' };
|
|
33
|
+
switch (next) {
|
|
34
|
+
case 'approved':
|
|
35
|
+
return { kind: 'start', step: 'approved' };
|
|
36
|
+
case 'in_progress':
|
|
37
|
+
return { kind: 'start', step: 'in_progress' };
|
|
38
|
+
case 'implemented':
|
|
39
|
+
return { kind: 'finish' };
|
|
40
|
+
case 'rejected':
|
|
41
|
+
return { kind: 'fail', reason: resolution ?? 'rejected' };
|
|
42
|
+
case 'deferred':
|
|
43
|
+
return { kind: 'cancel', reason: resolution ?? 'deferred' };
|
|
44
|
+
case 'proposed':
|
|
45
|
+
// Re-opening is not a legal transition; treat as no-op for v1.
|
|
46
|
+
return { kind: 'noop' };
|
|
47
|
+
default:
|
|
48
|
+
return { kind: 'noop' };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Owner-key for a proposal-flow. Deterministic — used as both the TaskFlow
|
|
53
|
+
* `ownerKey` and the basis of the `idempotencyKey`. Phase 3b's cutover
|
|
54
|
+
* gate relies on `evolution:cluster:<id>` being a stable bidirectional join.
|
|
55
|
+
*/
|
|
56
|
+
function ownerKeyForProposal(proposalId) {
|
|
57
|
+
return `evolution:cluster:${proposalId}`;
|
|
58
|
+
}
|
|
20
59
|
export class EvolutionManager {
|
|
21
60
|
stateDir;
|
|
22
61
|
config;
|
|
23
62
|
trustElevationTracker = null;
|
|
24
63
|
autonomousEvolution = null;
|
|
25
64
|
autonomyManager = null;
|
|
65
|
+
// ── TaskFlow Phase 3a dual-write fields ──────────────────────────
|
|
66
|
+
/** Registry handle (set when TaskFlow is enabled). */
|
|
67
|
+
taskFlowRegistry = null;
|
|
68
|
+
/** Per-process controller-instance id (server uuid). */
|
|
69
|
+
taskFlowControllerInstanceId = null;
|
|
70
|
+
/**
|
|
71
|
+
* Set to true by DivergenceChecker when state-divergence is detected.
|
|
72
|
+
* When true, EvolutionManager continues JSON-only writes and DOES NOT
|
|
73
|
+
* dual-write to TaskFlow. The signal is reset on next divergence pass that
|
|
74
|
+
* reports zero divergence. This is a signal-emitted brake, not a hard gate.
|
|
75
|
+
*/
|
|
76
|
+
taskFlowShadowWritesHalted = false;
|
|
77
|
+
/** Most recent reason for halting; surfaced in /health and tests. */
|
|
78
|
+
taskFlowShadowWritesHaltReason = null;
|
|
79
|
+
/**
|
|
80
|
+
* Source of the most recent halt. DivergenceChecker uses this to ensure it
|
|
81
|
+
* only auto-clears halts that DivergenceChecker itself imposed — an operator
|
|
82
|
+
* or sibling system's manual halt stays in place until that source clears it.
|
|
83
|
+
*/
|
|
84
|
+
taskFlowShadowWritesHaltSource = null;
|
|
26
85
|
constructor(config) {
|
|
27
86
|
this.config = config;
|
|
28
87
|
this.stateDir = config.stateDir;
|
|
29
88
|
}
|
|
89
|
+
// ── TaskFlow wiring ─────────────────────────────────────────────
|
|
90
|
+
/**
|
|
91
|
+
* Wire a TaskFlow registry for Phase 3a dual-write. After this call:
|
|
92
|
+
* - addProposal / updateProposalStatus shadow-write to TaskFlow
|
|
93
|
+
* - migrateExistingToTaskFlow() backfills any in-flight clusters
|
|
94
|
+
* - DivergenceChecker can call setShadowWritesHalted() to brake writes
|
|
95
|
+
*
|
|
96
|
+
* Idempotent — safe to call multiple times; subsequent calls overwrite.
|
|
97
|
+
*/
|
|
98
|
+
setTaskFlowRegistry(registry, controllerInstanceId) {
|
|
99
|
+
this.taskFlowRegistry = registry;
|
|
100
|
+
this.taskFlowControllerInstanceId = controllerInstanceId;
|
|
101
|
+
}
|
|
102
|
+
getTaskFlowRegistry() {
|
|
103
|
+
return this.taskFlowRegistry;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Halt or resume TaskFlow shadow-writes. Called by DivergenceChecker on a
|
|
107
|
+
* mismatch. Signal-vs-authority compliance: this is a signal-consumed brake,
|
|
108
|
+
* not a brittle blocker — the divergence checker decides, EvolutionManager
|
|
109
|
+
* obeys. JSON writes continue regardless (read-authoritative TaskFlow is
|
|
110
|
+
* shadow-mode in Phase 3a).
|
|
111
|
+
*
|
|
112
|
+
* The optional `source` parameter tags the halt with its origin so an
|
|
113
|
+
* automatic clearer (DivergenceChecker) only cancels halts it caused.
|
|
114
|
+
* Defaults to `'manual'` when omitted.
|
|
115
|
+
*/
|
|
116
|
+
setShadowWritesHalted(halted, reason, source) {
|
|
117
|
+
this.taskFlowShadowWritesHalted = halted;
|
|
118
|
+
this.taskFlowShadowWritesHaltReason = halted ? (reason ?? 'divergence-detected') : null;
|
|
119
|
+
this.taskFlowShadowWritesHaltSource = halted ? (source ?? 'manual') : null;
|
|
120
|
+
}
|
|
121
|
+
isShadowWritesHalted() {
|
|
122
|
+
return {
|
|
123
|
+
halted: this.taskFlowShadowWritesHalted,
|
|
124
|
+
reason: this.taskFlowShadowWritesHaltReason,
|
|
125
|
+
source: this.taskFlowShadowWritesHaltSource,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
taskFlowPrincipal() {
|
|
129
|
+
if (!this.taskFlowRegistry || !this.taskFlowControllerInstanceId)
|
|
130
|
+
return null;
|
|
131
|
+
return {
|
|
132
|
+
scope: 'controller',
|
|
133
|
+
controllerId: EVOLUTION_TASKFLOW_CONTROLLER_ID,
|
|
134
|
+
controllerInstanceId: this.taskFlowControllerInstanceId,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Best-effort dual-write to TaskFlow. NEVER throws to the caller — JSON
|
|
139
|
+
* remains the local durability path; TaskFlow is shadow until Phase 3b
|
|
140
|
+
* cutover. All TaskFlow errors land in console and the divergence checker
|
|
141
|
+
* picks up via state comparison.
|
|
142
|
+
*/
|
|
143
|
+
async dualWriteCreate(p) {
|
|
144
|
+
if (!this.taskFlowRegistry || this.taskFlowShadowWritesHalted)
|
|
145
|
+
return;
|
|
146
|
+
const principal = this.taskFlowPrincipal();
|
|
147
|
+
if (!principal || principal.scope !== 'controller')
|
|
148
|
+
return;
|
|
149
|
+
try {
|
|
150
|
+
await this.taskFlowRegistry.createFlow({
|
|
151
|
+
controllerId: EVOLUTION_TASKFLOW_CONTROLLER_ID,
|
|
152
|
+
controllerInstanceId: principal.controllerInstanceId,
|
|
153
|
+
ownerKey: ownerKeyForProposal(p.id),
|
|
154
|
+
idempotencyKey: `evolution-cluster-create-${p.id}`,
|
|
155
|
+
goal: p.title.slice(0, 1024),
|
|
156
|
+
currentStep: 'proposed',
|
|
157
|
+
stateJson: { proposalId: p.id, type: p.type, source: p.source },
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
// Swallow — JSON is the source of truth in Phase 3a.
|
|
162
|
+
this.logTaskFlowError('createFlow', p.id, err);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async dualWriteTransition(proposalId, action) {
|
|
166
|
+
if (!this.taskFlowRegistry || this.taskFlowShadowWritesHalted)
|
|
167
|
+
return;
|
|
168
|
+
if (action.kind === 'noop' || action.kind === 'create')
|
|
169
|
+
return;
|
|
170
|
+
const principal = this.taskFlowPrincipal();
|
|
171
|
+
if (!principal)
|
|
172
|
+
return;
|
|
173
|
+
const registry = this.taskFlowRegistry;
|
|
174
|
+
try {
|
|
175
|
+
// Look up current flow by owner key + deterministic idempotency key.
|
|
176
|
+
const existing = registry.findByIdempotency(EVOLUTION_TASKFLOW_CONTROLLER_ID, ownerKeyForProposal(proposalId), `evolution-cluster-create-${proposalId}`);
|
|
177
|
+
if (!existing) {
|
|
178
|
+
// No backfill yet — proposal exists in JSON but not TaskFlow. Skip
|
|
179
|
+
// the transition silently; the next migrate-existing pass will pick
|
|
180
|
+
// it up at its terminal state.
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const flow = registry.getFlow(existing.flowId, { bypassCache: true });
|
|
184
|
+
if (!flow)
|
|
185
|
+
return;
|
|
186
|
+
const expectedRevision = flow.revision;
|
|
187
|
+
switch (action.kind) {
|
|
188
|
+
case 'start': {
|
|
189
|
+
if (flow.status !== 'queued' && flow.status !== 'running')
|
|
190
|
+
return;
|
|
191
|
+
await registry.startStep({
|
|
192
|
+
flowId: flow.flowId,
|
|
193
|
+
expectedRevision,
|
|
194
|
+
principal,
|
|
195
|
+
currentStep: action.step,
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case 'finish': {
|
|
200
|
+
// Need flow in `running` state for finishFlow. If currently queued,
|
|
201
|
+
// promote first.
|
|
202
|
+
let rev = expectedRevision;
|
|
203
|
+
let cur = flow;
|
|
204
|
+
if (cur.status === 'queued') {
|
|
205
|
+
const r = await registry.startStep({
|
|
206
|
+
flowId: flow.flowId,
|
|
207
|
+
expectedRevision: rev,
|
|
208
|
+
principal,
|
|
209
|
+
currentStep: 'implementing',
|
|
210
|
+
});
|
|
211
|
+
cur = r.flow;
|
|
212
|
+
rev = r.flow.revision;
|
|
213
|
+
}
|
|
214
|
+
if (cur.status !== 'running')
|
|
215
|
+
return;
|
|
216
|
+
await registry.finishFlow({
|
|
217
|
+
flowId: flow.flowId,
|
|
218
|
+
expectedRevision: rev,
|
|
219
|
+
principal,
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case 'fail': {
|
|
224
|
+
let rev = expectedRevision;
|
|
225
|
+
let cur = flow;
|
|
226
|
+
// failFlow requires running|waiting. Promote queued → running.
|
|
227
|
+
if (cur.status === 'queued') {
|
|
228
|
+
const r = await registry.startStep({
|
|
229
|
+
flowId: flow.flowId,
|
|
230
|
+
expectedRevision: rev,
|
|
231
|
+
principal,
|
|
232
|
+
currentStep: 'reject-transition',
|
|
233
|
+
});
|
|
234
|
+
cur = r.flow;
|
|
235
|
+
rev = r.flow.revision;
|
|
236
|
+
}
|
|
237
|
+
if (cur.status !== 'running' && cur.status !== 'waiting')
|
|
238
|
+
return;
|
|
239
|
+
await registry.failFlow({
|
|
240
|
+
flowId: flow.flowId,
|
|
241
|
+
expectedRevision: rev,
|
|
242
|
+
principal,
|
|
243
|
+
failureReason: action.reason,
|
|
244
|
+
});
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
case 'cancel': {
|
|
248
|
+
if (flow.status === 'succeeded' || flow.status === 'failed' ||
|
|
249
|
+
flow.status === 'cancelled' || flow.status === 'lost')
|
|
250
|
+
return;
|
|
251
|
+
// Two-phase: request, then cancel.
|
|
252
|
+
const r = await registry.requestFlowCancel({
|
|
253
|
+
flowId: flow.flowId,
|
|
254
|
+
expectedRevision,
|
|
255
|
+
requesterOrigin: { kind: 'system', id: 'EvolutionManager' },
|
|
256
|
+
});
|
|
257
|
+
await registry.cancelFlow({
|
|
258
|
+
flowId: flow.flowId,
|
|
259
|
+
expectedRevision: r.flow.revision,
|
|
260
|
+
principal,
|
|
261
|
+
});
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
this.logTaskFlowError(`transition:${action.kind}`, proposalId, err);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
logTaskFlowError(op, proposalId, err) {
|
|
271
|
+
if (err instanceof TaskFlowError) {
|
|
272
|
+
// OCC conflicts and invalid-transition errors are expected during
|
|
273
|
+
// races and concurrent updates — log at debug level only.
|
|
274
|
+
console.warn(`[EvolutionManager] taskflow ${op} for ${proposalId} skipped: ${err.code} (${err.message})`);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
console.warn(`[EvolutionManager] taskflow ${op} for ${proposalId} unexpected error:`, err instanceof Error ? err.message : err);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Backfill all in-flight proposals (status !== 'implemented' && !== 'rejected')
|
|
282
|
+
* into TaskFlow. Idempotent via `findIdempotent` on
|
|
283
|
+
* `evolution:cluster:<proposalId>` ownerKey. Safe to call multiple times.
|
|
284
|
+
*
|
|
285
|
+
* Returns counts: created, alreadyExisted, advanced (transitioned beyond
|
|
286
|
+
* 'queued' to match the proposal's current status).
|
|
287
|
+
*/
|
|
288
|
+
async migrateExistingToTaskFlow() {
|
|
289
|
+
if (!this.taskFlowRegistry) {
|
|
290
|
+
return { created: 0, alreadyExisted: 0, advanced: 0, skipped: 0 };
|
|
291
|
+
}
|
|
292
|
+
const principal = this.taskFlowPrincipal();
|
|
293
|
+
if (!principal || principal.scope !== 'controller') {
|
|
294
|
+
return { created: 0, alreadyExisted: 0, advanced: 0, skipped: 0 };
|
|
295
|
+
}
|
|
296
|
+
const registry = this.taskFlowRegistry;
|
|
297
|
+
const state = this.loadEvolution();
|
|
298
|
+
let created = 0;
|
|
299
|
+
let alreadyExisted = 0;
|
|
300
|
+
let advanced = 0;
|
|
301
|
+
let skipped = 0;
|
|
302
|
+
for (const p of state.proposals) {
|
|
303
|
+
try {
|
|
304
|
+
const existing = registry.findByIdempotency(EVOLUTION_TASKFLOW_CONTROLLER_ID, ownerKeyForProposal(p.id), `evolution-cluster-create-${p.id}`);
|
|
305
|
+
let flow;
|
|
306
|
+
if (existing) {
|
|
307
|
+
alreadyExisted++;
|
|
308
|
+
flow = existing;
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
const r = await registry.createFlow({
|
|
312
|
+
controllerId: EVOLUTION_TASKFLOW_CONTROLLER_ID,
|
|
313
|
+
controllerInstanceId: principal.controllerInstanceId,
|
|
314
|
+
ownerKey: ownerKeyForProposal(p.id),
|
|
315
|
+
idempotencyKey: `evolution-cluster-create-${p.id}`,
|
|
316
|
+
goal: p.title.slice(0, 1024),
|
|
317
|
+
currentStep: 'proposed',
|
|
318
|
+
stateJson: { proposalId: p.id, type: p.type, source: p.source },
|
|
319
|
+
});
|
|
320
|
+
if (r.created)
|
|
321
|
+
created++;
|
|
322
|
+
else
|
|
323
|
+
alreadyExisted++;
|
|
324
|
+
flow = r.flow;
|
|
325
|
+
}
|
|
326
|
+
// Catch the flow up to the proposal's current status.
|
|
327
|
+
const advancedThis = await this.catchUpFlowToStatus(flow, p, principal);
|
|
328
|
+
if (advancedThis)
|
|
329
|
+
advanced++;
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
skipped++;
|
|
333
|
+
this.logTaskFlowError('migrate', p.id, err);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return { created, alreadyExisted, advanced, skipped };
|
|
337
|
+
}
|
|
338
|
+
/** Catch a flow record up to the proposal's current status. */
|
|
339
|
+
async catchUpFlowToStatus(flow, p, principal) {
|
|
340
|
+
if (!this.taskFlowRegistry)
|
|
341
|
+
return false;
|
|
342
|
+
const registry = this.taskFlowRegistry;
|
|
343
|
+
let cur = registry.getFlow(flow.flowId, { bypassCache: true }) ?? flow;
|
|
344
|
+
let mutated = false;
|
|
345
|
+
const target = p.status;
|
|
346
|
+
// Already terminal — leave alone.
|
|
347
|
+
if (cur.status === 'succeeded' || cur.status === 'failed' ||
|
|
348
|
+
cur.status === 'cancelled' || cur.status === 'lost') {
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
// Promote queued → running if target is running/terminal-success/failed.
|
|
352
|
+
if (cur.status === 'queued' && (target === 'approved' || target === 'in_progress' ||
|
|
353
|
+
target === 'implemented' || target === 'rejected' || target === 'deferred')) {
|
|
354
|
+
const step = target === 'in_progress' ? 'in_progress'
|
|
355
|
+
: target === 'approved' ? 'approved' : 'catch-up';
|
|
356
|
+
const r = await registry.startStep({
|
|
357
|
+
flowId: cur.flowId,
|
|
358
|
+
expectedRevision: cur.revision,
|
|
359
|
+
principal,
|
|
360
|
+
currentStep: step,
|
|
361
|
+
});
|
|
362
|
+
cur = r.flow;
|
|
363
|
+
mutated = true;
|
|
364
|
+
}
|
|
365
|
+
// Finalize.
|
|
366
|
+
if (target === 'implemented' && cur.status === 'running') {
|
|
367
|
+
await registry.finishFlow({
|
|
368
|
+
flowId: cur.flowId,
|
|
369
|
+
expectedRevision: cur.revision,
|
|
370
|
+
principal,
|
|
371
|
+
});
|
|
372
|
+
mutated = true;
|
|
373
|
+
}
|
|
374
|
+
else if (target === 'rejected' &&
|
|
375
|
+
(cur.status === 'running' || cur.status === 'waiting')) {
|
|
376
|
+
await registry.failFlow({
|
|
377
|
+
flowId: cur.flowId,
|
|
378
|
+
expectedRevision: cur.revision,
|
|
379
|
+
principal,
|
|
380
|
+
failureReason: p.resolution ?? 'rejected',
|
|
381
|
+
});
|
|
382
|
+
mutated = true;
|
|
383
|
+
}
|
|
384
|
+
else if (target === 'deferred' &&
|
|
385
|
+
(cur.status === 'running' || cur.status === 'waiting' || cur.status === 'queued')) {
|
|
386
|
+
const r = await registry.requestFlowCancel({
|
|
387
|
+
flowId: cur.flowId,
|
|
388
|
+
expectedRevision: cur.revision,
|
|
389
|
+
requesterOrigin: { kind: 'system', id: 'EvolutionManager' },
|
|
390
|
+
});
|
|
391
|
+
await registry.cancelFlow({
|
|
392
|
+
flowId: cur.flowId,
|
|
393
|
+
expectedRevision: r.flow.revision,
|
|
394
|
+
principal,
|
|
395
|
+
});
|
|
396
|
+
mutated = true;
|
|
397
|
+
}
|
|
398
|
+
return mutated;
|
|
399
|
+
}
|
|
30
400
|
/**
|
|
31
401
|
* Wire adaptive autonomy modules for runtime integration.
|
|
32
402
|
* - TrustElevationTracker: receives proposal approval/rejection events
|
|
@@ -143,6 +513,8 @@ export class EvolutionManager {
|
|
|
143
513
|
};
|
|
144
514
|
state.proposals.push(proposal);
|
|
145
515
|
this.saveEvolution(state);
|
|
516
|
+
// TaskFlow Phase 3a shadow-write — best-effort, JSON remains source of truth.
|
|
517
|
+
void this.dualWriteCreate(proposal);
|
|
146
518
|
return proposal;
|
|
147
519
|
}
|
|
148
520
|
updateProposalStatus(id, status, resolution) {
|
|
@@ -150,6 +522,7 @@ export class EvolutionManager {
|
|
|
150
522
|
const proposal = state.proposals.find(p => p.id === id);
|
|
151
523
|
if (!proposal)
|
|
152
524
|
return false;
|
|
525
|
+
const prevStatus = proposal.status;
|
|
153
526
|
proposal.status = status;
|
|
154
527
|
if (resolution)
|
|
155
528
|
proposal.resolution = resolution;
|
|
@@ -161,6 +534,9 @@ export class EvolutionManager {
|
|
|
161
534
|
const decision = status === 'approved' ? 'approved' : 'rejected';
|
|
162
535
|
this.trustElevationTracker.recordProposalDecision(proposal, decision, false);
|
|
163
536
|
}
|
|
537
|
+
// TaskFlow Phase 3a shadow-write — best-effort, JSON remains source of truth.
|
|
538
|
+
const action = statusToFlowAction(prevStatus, status, resolution);
|
|
539
|
+
void this.dualWriteTransition(id, action);
|
|
164
540
|
return true;
|
|
165
541
|
}
|
|
166
542
|
/**
|