@kognai/orchestrator-core 0.1.3 → 0.1.4

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/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ export * from './lib/trust-score-updater';
23
23
  export * from './lib/citizenship';
24
24
  export * from './lib/agent-registry';
25
25
  export * from './lib/sprint-state';
26
+ export * from './lib/build-triage';
26
27
  export * as ksl from './lib/ksl';
27
28
  export * from './lib/wallet-state';
28
29
  export * from './lib/ceo-wallet';
package/dist/index.js CHANGED
@@ -82,6 +82,9 @@ __exportStar(require("./lib/citizenship"), exports);
82
82
  __exportStar(require("./lib/agent-registry"), exports);
83
83
  // TICKET-098 sprint runtime-state split (committed defs vs gitignored status).
84
84
  __exportStar(require("./lib/sprint-state"), exports);
85
+ // TICKET-234 build triage seam — classifyBuildPath (ceremony-depth axis) +
86
+ // logBuildTriage. Consumed by the orchestrator run loop AND by kognai-build.
87
+ __exportStar(require("./lib/build-triage"), exports);
85
88
  // Phase 3b-3 Wave B: KSL capture cluster (session records + error log + tap).
86
89
  // Namespaced ('ksl.tapAttempt', 'ksl.writeRecord', 'ksl.record', …) to keep the
87
90
  // generic 'record' export off the package's flat public surface.
@@ -0,0 +1,27 @@
1
+ export type BuildPath = 'regulated' | 'fast';
2
+ export interface BuildTriageResult {
3
+ path: BuildPath;
4
+ reason: string;
5
+ triggers: string[];
6
+ sensitive: boolean;
7
+ sovereign: boolean;
8
+ complexity: 'low' | 'medium' | 'high';
9
+ taskCount: number;
10
+ codeFileCount: number;
11
+ }
12
+ /**
13
+ * Classify a build into a ceremony-depth path. Pure + synchronous: it only reads
14
+ * the sprint JSON + task list, so it adds negligible cost relative to the
15
+ * ceremony it gates. Default-to-regulated on uncertainty.
16
+ *
17
+ * @param sprint the raw sprint object (may carry regulated/strict/fast_track/sovereign flags)
18
+ * @param tasks the loaded task list (each with deliverables/context/type)
19
+ */
20
+ export declare function classifyBuildPath(sprint: any, tasks: any[]): BuildTriageResult;
21
+ /**
22
+ * Append the triage decision to logs/routing/triage.jsonl (location-independent
23
+ * via engine-paths). Best-effort: never throws into the run loop. This is the
24
+ * audit trail required by the ticket — evidence that triage routes correctly and
25
+ * the means to audit any fast-tracked build later.
26
+ */
27
+ export declare function logBuildTriage(sprintId: string, result: BuildTriageResult): void;
@@ -0,0 +1,202 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.classifyBuildPath = classifyBuildPath;
4
+ exports.logBuildTriage = logBuildTriage;
5
+ /**
6
+ * build-triage.ts — TICKET-234: pre-orchestrator build triage (ceremony-depth axis).
7
+ *
8
+ * The orchestrator historically ran the SAME full ceremony for every build
9
+ * regardless of complexity or sensitivity (CEO assessment → execution →
10
+ * dual-supervisor review → CTO data analysis → CEO proposal cycle → post-sprint
11
+ * + agent minting). A 3-line `addTwo` cost 171s + the entire ceremony.
12
+ *
13
+ * This module adds a cheap classifier that runs BEFORE the orchestrator hands
14
+ * off to the swarm and routes the build to one of two execution paths:
15
+ *
16
+ * - REGULATED (full ceremony) — CEO · CTO governance gate · dual-supervisor
17
+ * review · reconciliation · the full CTO/CEO proposal cycle. Templates apply
18
+ * on this path only. For sensitive / sovereign / complex builds.
19
+ *
20
+ * - FAST — coder → single-supervisor review → the minimum safety/security/
21
+ * quality gate (QA gate: typecheck/compile/no-secrets, always on). No
22
+ * dual-supervisor, no CTO/CEO post-sprint ceremony, no templates. For
23
+ * simple, low-stakes, single-file, non-sensitive builds.
24
+ *
25
+ * It is the CEREMONY-DEPTH axis. It composes WITH `assessTaskComplexity`
26
+ * (engine-helpers), which scores the MODEL-ROUTING axis — they are orthogonal:
27
+ * a fast-tracked build can still route to a cloud model, and a regulated build
28
+ * can still run local. This stays a PURE HEURISTIC (no LLM call) so the triage
29
+ * never reintroduces the overhead it removes.
30
+ *
31
+ * Safety invariant: DEFAULT TO REGULATED on any uncertainty. `fast` is returned
32
+ * only when the build is provably simple AND non-sensitive AND non-sovereign.
33
+ * A `fast_track` opt-in never lowers the safety floor — sensitivity/complexity
34
+ * still force REGULATED.
35
+ */
36
+ const fs_1 = require("fs");
37
+ const path_1 = require("path");
38
+ const engine_paths_1 = require("./engine-paths");
39
+ // Sensitivity keywords — PHI/PII, payments/financial, auth/secrets/credentials,
40
+ // crypto/on-chain/contracts, destructive or privileged operations. A match in a
41
+ // deliverable path, task context, or sprint goal forces REGULATED.
42
+ const SENSITIVE_KEYWORDS = [
43
+ // auth / secrets / credentials
44
+ 'auth', 'secret', 'credential', 'password', 'passwd', 'api[_-]?key', 'apikey',
45
+ 'access[_-]?token', 'private[_-]?key', 'privatekey', 'seed[_-]?phrase', 'mnemonic',
46
+ 'oauth', 'jwt', 'session[_-]?token', 'vault',
47
+ // payments / financial
48
+ 'payment', 'billing', 'invoice', 'payout', 'stripe', 'paypal', 'charge', 'refund',
49
+ 'kyc', 'ledger', 'wallet', 'pricing',
50
+ // crypto / on-chain / contracts
51
+ 'x402', 'crypto', 'on[_-]?chain', 'onchain', 'blockchain', 'smart[_-]?contract',
52
+ 'erc20', 'erc-20', 'usdc', '\\$kog', 'viem', 'web3',
53
+ // PHI / PII / regulated data
54
+ '\\bphi\\b', '\\bpii\\b', 'gdpr', 'hipaa', 'personal[_-]?data', 'health[_-]?record',
55
+ // schema / data migrations (high-blast-radius, hard to roll back)
56
+ 'migration', 'prisma/migrations', 'drop[_-]?table', 'alter[_-]?table',
57
+ ];
58
+ // Destructive / privileged operations — context-level matches force REGULATED.
59
+ const DESTRUCTIVE_KEYWORDS = [
60
+ 'rm\\s+-rf', 'drop\\s+table', 'delete\\s+from', 'truncate\\s+table',
61
+ 'force[_-]?push', '--force', 'chmod\\s+777', 'sudo\\b', 'kill\\s+-9',
62
+ ];
63
+ // Sensitive directories — a deliverable under one of these forces REGULATED.
64
+ const SENSITIVE_DIRS = [
65
+ 'banks/', 'wallet', 'ceo-wallet', 'gates/', 'policy/', 'contracts/', 'omel/',
66
+ 'x402-base/', 'prisma/', 'credential', '.env', 'auth/', 'secrets/',
67
+ ];
68
+ // Architectural / cross-cutting keywords — a match means the change is not a
69
+ // simple single-concern build → REGULATED.
70
+ const ARCHITECTURAL_KEYWORDS = [
71
+ 'architect', 'refactor', 'migrate', 'cross[_-]?cutting', 'system[_-]?wide',
72
+ 'redesign', 'database', 'schema', 'infrastructure', 'orchestrat', 'pipeline',
73
+ 'framework', 'breaking[_-]?change', 'rewrite',
74
+ ];
75
+ function compile(words) {
76
+ return new RegExp(`(${words.join('|')})`, 'i');
77
+ }
78
+ const SENSITIVE_RE = compile(SENSITIVE_KEYWORDS);
79
+ const DESTRUCTIVE_RE = compile(DESTRUCTIVE_KEYWORDS);
80
+ const ARCH_RE = compile(ARCHITECTURAL_KEYWORDS);
81
+ function asBool(v) {
82
+ return v === true || v === 'true' || v === 1 || v === '1';
83
+ }
84
+ /**
85
+ * Classify a build into a ceremony-depth path. Pure + synchronous: it only reads
86
+ * the sprint JSON + task list, so it adds negligible cost relative to the
87
+ * ceremony it gates. Default-to-regulated on uncertainty.
88
+ *
89
+ * @param sprint the raw sprint object (may carry regulated/strict/fast_track/sovereign flags)
90
+ * @param tasks the loaded task list (each with deliverables/context/type)
91
+ */
92
+ function classifyBuildPath(sprint, tasks) {
93
+ const triggers = [];
94
+ const taskList = Array.isArray(tasks) ? tasks : [];
95
+ const taskCount = taskList.length;
96
+ // Gather all the text we scan for sensitivity/architecture signals.
97
+ const deliverablePaths = [];
98
+ const codeFiles = [];
99
+ for (const t of taskList) {
100
+ const d = (t && t.deliverables) || {};
101
+ const code = Array.isArray(d.code) ? d.code : [];
102
+ const tests = Array.isArray(d.tests) ? d.tests : [];
103
+ const docs = Array.isArray(d.docs) ? d.docs : [];
104
+ codeFiles.push(...code);
105
+ deliverablePaths.push(...code, ...tests, ...docs);
106
+ }
107
+ const codeFileCount = new Set(codeFiles).size;
108
+ const contextText = taskList
109
+ .map((t) => `${(t && (t.context || t.title || t.id)) || ''} ${(t && (t.type || t.task_type)) || ''}`)
110
+ .join(' ');
111
+ const goalText = `${sprint?.name || sprint?.title || ''} ${sprint?.goal || sprint?.description || ''}`;
112
+ const pathText = deliverablePaths.join(' ');
113
+ const haystack = `${goalText} ${contextText} ${pathText}`;
114
+ // ── Explicit flags ────────────────────────────────────────────────────────
115
+ const forcedRegulated = asBool(sprint?.regulated) || asBool(sprint?.strict) || asBool(process.env.KOGNAI_FORCE_REGULATED);
116
+ const requestedFast = asBool(sprint?.fast_track) || asBool(sprint?.fast);
117
+ if (forcedRegulated)
118
+ triggers.push('explicit:regulated');
119
+ // ── Sensitivity ───────────────────────────────────────────────────────────
120
+ let sensitive = false;
121
+ if (SENSITIVE_RE.test(haystack)) {
122
+ sensitive = true;
123
+ triggers.push(`sensitive:keyword(${(haystack.match(SENSITIVE_RE) || [])[1]})`);
124
+ }
125
+ if (DESTRUCTIVE_RE.test(haystack)) {
126
+ sensitive = true;
127
+ triggers.push(`sensitive:destructive(${(haystack.match(DESTRUCTIVE_RE) || [])[1]})`);
128
+ }
129
+ const dirHit = SENSITIVE_DIRS.find((dir) => pathText.toLowerCase().includes(dir.toLowerCase()));
130
+ if (dirHit) {
131
+ sensitive = true;
132
+ triggers.push(`sensitive:path(${dirHit})`);
133
+ }
134
+ // ── Sovereign / regulated domain ──────────────────────────────────────────
135
+ const sovereign = asBool(sprint?.sovereign) || asBool(sprint?.regulated_domain);
136
+ if (sovereign)
137
+ triggers.push('sovereign');
138
+ // ── Complexity (ceremony-depth, NOT model routing) ────────────────────────
139
+ const estimated = String(sprint?.estimated_complexity || '').toLowerCase();
140
+ const archHit = ARCH_RE.test(haystack);
141
+ if (archHit)
142
+ triggers.push(`complex:architectural(${(haystack.match(ARCH_RE) || [])[1]})`);
143
+ if (taskCount > 1)
144
+ triggers.push(`complex:multi-task(${taskCount})`);
145
+ if (codeFileCount > 1)
146
+ triggers.push(`complex:multi-file(${codeFileCount})`);
147
+ if (estimated === 'high')
148
+ triggers.push('complex:estimated-high');
149
+ let complexity = 'low';
150
+ if (archHit || taskCount > 2 || codeFileCount > 2 || estimated === 'high')
151
+ complexity = 'high';
152
+ else if (taskCount > 1 || codeFileCount > 1 || estimated === 'medium')
153
+ complexity = 'medium';
154
+ // ── Decision (default-to-regulated) ───────────────────────────────────────
155
+ // A build is FAST-eligible only if it is provably simple AND nothing sensitive
156
+ // / sovereign / forced fired. Anything ambiguous → REGULATED.
157
+ const isSimple = taskCount === 1 && codeFileCount <= 1 && !archHit && estimated !== 'high';
158
+ const blockFast = forcedRegulated || sensitive || sovereign || !isSimple;
159
+ let path;
160
+ let reason;
161
+ if (blockFast) {
162
+ path = 'regulated';
163
+ reason =
164
+ forcedRegulated ? 'explicit --regulated/--strict flag'
165
+ : sensitive ? `sensitive build (${triggers.filter((t) => t.startsWith('sensitive')).join(', ')})`
166
+ : sovereign ? 'sovereign / regulated-domain build'
167
+ : `complexity above fast-track threshold (${triggers.filter((t) => t.startsWith('complex')).join(', ') || 'multi-concern'})`;
168
+ }
169
+ else {
170
+ path = 'fast';
171
+ reason = requestedFast
172
+ ? 'fast-track opt-in; simple non-sensitive single-file build'
173
+ : 'simple non-sensitive single-file build → fast track';
174
+ }
175
+ return { path, reason, triggers, sensitive, sovereign, complexity, taskCount, codeFileCount };
176
+ }
177
+ /**
178
+ * Append the triage decision to logs/routing/triage.jsonl (location-independent
179
+ * via engine-paths). Best-effort: never throws into the run loop. This is the
180
+ * audit trail required by the ticket — evidence that triage routes correctly and
181
+ * the means to audit any fast-tracked build later.
182
+ */
183
+ function logBuildTriage(sprintId, result) {
184
+ try {
185
+ const file = (0, path_1.join)((0, engine_paths_1.resolveEnginePaths)().logs, 'routing', 'triage.jsonl');
186
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(file), { recursive: true });
187
+ const row = {
188
+ sprint_id: sprintId,
189
+ path: result.path,
190
+ reason: result.reason,
191
+ triggers: result.triggers,
192
+ sensitive: result.sensitive,
193
+ sovereign: result.sovereign,
194
+ complexity: result.complexity,
195
+ task_count: result.taskCount,
196
+ code_file_count: result.codeFileCount,
197
+ logged_at: new Date().toISOString(),
198
+ };
199
+ (0, fs_1.appendFileSync)(file, JSON.stringify(row) + '\n');
200
+ }
201
+ catch { /* non-fatal — audit log must never break a run */ }
202
+ }
@@ -7,6 +7,8 @@ export declare class Orchestrator {
7
7
  private supervisor2;
8
8
  private agents;
9
9
  private tasks;
10
+ private buildPath;
11
+ private triage;
10
12
  private stats;
11
13
  private taskRuns;
12
14
  /**
@@ -36,6 +36,7 @@ const engine_agents_1 = require("./engine-agents");
36
36
  const engine_coding_agent_1 = require("./engine-coding-agent");
37
37
  const engine_loaders_1 = require("./engine-loaders");
38
38
  const orchestrate_engine_1 = require("./orchestrate-engine");
39
+ const build_triage_1 = require("./build-triage");
39
40
  class Orchestrator {
40
41
  spawnGate;
41
42
  ceo;
@@ -44,6 +45,11 @@ class Orchestrator {
44
45
  supervisor2;
45
46
  agents = new Map();
46
47
  tasks = [];
48
+ // TICKET-234: ceremony-depth path for this run. 'regulated' = full ceremony
49
+ // (default, safe); 'fast' = coder → single-supervisor → QA safety floor only.
50
+ // Set in run() by classifyBuildPath after tasks load. Defaults to regulated.
51
+ buildPath = 'regulated';
52
+ triage = null;
47
53
  stats = { tasksExecuted: 0, approved: 0, rejected: 0, totalTokens: 0, conflicts: 0, escalations: 0 };
48
54
  // Per-task structured run records — written to swarm run report at end of sprint
49
55
  taskRuns = [];
@@ -219,6 +225,13 @@ class Orchestrator {
219
225
  // Normalize priority: sprint JSON may omit it
220
226
  if (!task.priority)
221
227
  task.priority = 'medium';
228
+ // Normalize status: sprint JSON may omit it. Without a status the task is
229
+ // silently skipped in Phase 2 (`if (task.status !== 'pending') continue`),
230
+ // so the full ceremony runs but executes 0 tasks. Default a MISSING status
231
+ // to 'pending'; explicit terminal states (done/skipped/rejected/approved)
232
+ // are preserved so re-runs don't re-execute finished work.
233
+ if (!task.status)
234
+ task.status = 'pending';
222
235
  // Fix: task_target used as file path (e.g., 'scripts/lib/foo.ts') must be cleared
223
236
  // so it doesn't confuse the routing switch which expects: local|cloud-code|cloud-exec|cloud-post
224
237
  const VALID_ROUTING_TARGETS = ['local', 'cloud-code', 'cloud-exec', 'cloud-post'];
@@ -404,17 +417,23 @@ ONLY output the JSON array. No markdown, no explanation.`;
404
417
  monotask_state_machine_1.MonotaskSM.release(subtask.agent, subtask.id, `no files: ${inferred.slice(0, 60)}`);
405
418
  return false;
406
419
  }
407
- // Dual supervisor review
408
- const [review1, review2] = await Promise.all([
409
- this.supervisor.reviewTask(subtask, result.files),
410
- this.supervisor2.reviewTask(subtask, result.files),
411
- ]);
412
- const dualResult = await (0, engine_agents_1.reconcileSupervisorReviews)(review1, review2, subtask, this.ceo);
413
- const review = dualResult.finalReview;
414
- if (!dualResult.consensus)
415
- this.stats.conflicts++;
416
- if (dualResult.escalatedToCEO)
417
- this.stats.escalations++;
420
+ // Supervisor review. TICKET-234: fast track → single supervisor; regulated → dual.
421
+ let review;
422
+ if (this.buildPath === 'fast') {
423
+ review = await this.supervisor.reviewTask(subtask, result.files);
424
+ }
425
+ else {
426
+ const [review1, review2] = await Promise.all([
427
+ this.supervisor.reviewTask(subtask, result.files),
428
+ this.supervisor2.reviewTask(subtask, result.files),
429
+ ]);
430
+ const dualResult = await (0, engine_agents_1.reconcileSupervisorReviews)(review1, review2, subtask, this.ceo);
431
+ review = dualResult.finalReview;
432
+ if (!dualResult.consensus)
433
+ this.stats.conflicts++;
434
+ if (dualResult.escalatedToCEO)
435
+ this.stats.escalations++;
436
+ }
418
437
  lastReview = review;
419
438
  // TICKET-214: instrument SUB-TASK attempts. The MAIN task loop already taps KSL +
420
439
  // records reputation, but the split sub-task loop did neither — so split sprints
@@ -787,18 +806,28 @@ ONLY output the JSON array. No markdown, no explanation.`;
787
806
  return;
788
807
  }
789
808
  (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ` [QA-gate] PASS — ${qaResult.reason}`);
790
- // Dual Supervisor review (DeepSeek/ClawRouter + Haiku in parallel)
809
+ // Supervisor review. TICKET-234: fast track runs ONE supervisor (no dual
810
+ // review / reconcile / CEO escalation); regulated runs the dual-supervisor
811
+ // ceremony. The QA gate above already enforced the safety floor
812
+ // (typecheck/compile/no-secrets) on both paths.
791
813
  task.status = 'review';
792
- const [review1, review2] = await Promise.all([
793
- this.supervisor.reviewTask(task, result.files),
794
- this.supervisor2.reviewTask(task, result.files),
795
- ]);
796
- const dualResult = await (0, engine_agents_1.reconcileSupervisorReviews)(review1, review2, task, this.ceo);
797
- const review = dualResult.finalReview;
798
- if (!dualResult.consensus)
799
- this.stats.conflicts++;
800
- if (dualResult.escalatedToCEO)
801
- this.stats.escalations++;
814
+ let review;
815
+ if (this.buildPath === 'fast') {
816
+ review = await this.supervisor.reviewTask(task, result.files);
817
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' [fast-track] single-supervisor review (dual-supervisor skipped)');
818
+ }
819
+ else {
820
+ const [review1, review2] = await Promise.all([
821
+ this.supervisor.reviewTask(task, result.files),
822
+ this.supervisor2.reviewTask(task, result.files),
823
+ ]);
824
+ const dualResult = await (0, engine_agents_1.reconcileSupervisorReviews)(review1, review2, task, this.ceo);
825
+ review = dualResult.finalReview;
826
+ if (!dualResult.consensus)
827
+ this.stats.conflicts++;
828
+ if (dualResult.escalatedToCEO)
829
+ this.stats.escalations++;
830
+ }
802
831
  lastReview = review;
803
832
  task.output = { files: result.files, commit: '', model: result.model, review };
804
833
  // CTO-006 telemetry-blackout hotfix (2026-05-29, Slice A of TICKET-135):
@@ -1050,6 +1079,28 @@ ONLY output the JSON array. No markdown, no explanation.`;
1050
1079
  await mc.disconnect().catch(() => { });
1051
1080
  return;
1052
1081
  }
1082
+ // ── BUILD TRIAGE — TICKET-234 (ceremony-depth routing) ─────────────────
1083
+ // Classify the build BEFORE the swarm ceremony: simple, non-sensitive,
1084
+ // single-file builds take the FAST path (single-supervisor + QA safety
1085
+ // floor, no CTO/CEO post-sprint ceremony, no templates); everything
1086
+ // sensitive/sovereign/complex/ambiguous takes the REGULATED full ceremony.
1087
+ // Pure heuristic — negligible cost vs the ceremony it gates. SOVEREIGN_MODE
1088
+ // always forces regulated: a sovereign build is regulated by definition.
1089
+ {
1090
+ const triageSprint = JSON.parse((0, fs_1.readFileSync)(process.argv[2] || 'sprints/current.json', 'utf-8'));
1091
+ this.triage = (0, build_triage_1.classifyBuildPath)(triageSprint, this.tasks);
1092
+ if (engine_primitives_1.SOVEREIGN_MODE && this.triage.path === 'fast') {
1093
+ this.triage = { ...this.triage, path: 'regulated', reason: 'sovereign mode → regulated by definition', triggers: [...this.triage.triggers, 'sovereign:mode'], sovereign: true };
1094
+ }
1095
+ this.buildPath = this.triage.path;
1096
+ (0, build_triage_1.logBuildTriage)(_evtSprintId, this.triage);
1097
+ const col = this.buildPath === 'fast' ? engine_primitives_1.c.green : engine_primitives_1.c.magenta;
1098
+ (0, engine_primitives_1.log)(col, `\n--- Build Triage: ${this.buildPath.toUpperCase()} ---`);
1099
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ` ${this.triage.reason}`);
1100
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ` complexity=${this.triage.complexity} tasks=${this.triage.taskCount} code-files=${this.triage.codeFileCount}${this.triage.triggers.length ? ` · triggers: ${this.triage.triggers.join(', ')}` : ''}`);
1101
+ if (this.buildPath === 'fast')
1102
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' Fast track: single-supervisor + QA safety floor; skipping dual-supervisor, CTO/CEO post-sprint ceremony, and templates.');
1103
+ }
1053
1104
  // ── CTO APPROVAL GATE — Exec Protocol §17 ──────────────────────────────
1054
1105
  // Every autonomous sprint must be approved by the CTO agent before execution.
1055
1106
  // Human-submitted sprints (source: 'human') are auto-approved.
@@ -1100,8 +1151,14 @@ ONLY output the JSON array. No markdown, no explanation.`;
1100
1151
  }
1101
1152
  // ── End CTO Gate ────────────────────────────────────────────────────────
1102
1153
  // 2. CEO initial assessment (B.14: once per sprint, not once per conflict)
1103
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 1: CEO Initial Assessment ---');
1104
- await this.ceo.reviewSprintProgress(this.tasks);
1154
+ // TICKET-234: regulated path only the fast track skips the CEO ceremony.
1155
+ if (this.buildPath === 'regulated') {
1156
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 1: CEO Initial Assessment ---');
1157
+ await this.ceo.reviewSprintProgress(this.tasks);
1158
+ }
1159
+ else {
1160
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, '\n--- Phase 1: CEO Initial Assessment — SKIPPED (fast track) ---');
1161
+ }
1105
1162
  const _ceoIntentDone = true; // flag for Phase 5: only re-run if ≥2 rejected
1106
1163
  // 3. Execute coding tasks with review loop
1107
1164
  (0, engine_primitives_1.log)(engine_primitives_1.c.blue, '\n--- Phase 2: Sprint Execution ---');
@@ -1220,79 +1277,91 @@ ONLY output the JSON array. No markdown, no explanation.`;
1220
1277
  }
1221
1278
  }
1222
1279
  // 4. CTO data-driven analysis + CMO reports
1223
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, '\n--- Phase 3: CTO Data-Driven Analysis + CMO Reports ---');
1280
+ // TICKET-234: Phases 3–6 (CTO/CEO governance ceremony + agent minting +
1281
+ // daily report) run on the REGULATED path only. Fast track ships after the
1282
+ // per-task review + QA safety floor; the financial telemetry below (wallet
1283
+ // burn + budget freeze/warning) still runs on both paths.
1224
1284
  let ctoReport = { summary: '', proposals: [], metrics_reviewed: [] };
1225
1285
  let ctoDecisions = '';
1226
1286
  let cmoReports = '';
1227
- try {
1228
- ctoReport = await this.cto.analyze();
1229
- // Load CMO reports (produced independently by Manus AI runner)
1230
- cmoReports = (0, engine_loaders_1.loadCMOReports)();
1231
- if (cmoReports) {
1232
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ' CMO reports found — will include in CEO review');
1233
- }
1234
- else {
1235
- (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' No CMO reports available yet');
1236
- }
1237
- // Load CTO tech-watch reports (produced independently by run-cto-techwatch.ts)
1238
- const ctoTechWatch = (0, engine_loaders_1.loadCTOTechWatchReports)();
1239
- if (ctoTechWatch) {
1240
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' CTO tech-watch reports found — will include in CEO review');
1241
- cmoReports = cmoReports ? cmoReports + '\n\n' + ctoTechWatch : ctoTechWatch;
1242
- }
1243
- else {
1244
- (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' No CTO tech-watch reports available yet');
1245
- }
1246
- // Load Grok intelligence feed (Grok AI monitors X/Twitter for OpenClaw news)
1247
- const grokFeed = (0, engine_loaders_1.loadGrokFeed)();
1248
- if (grokFeed) {
1249
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ' Grok intelligence feed found — will include in CEO review');
1250
- cmoReports = cmoReports ? cmoReports + '\n\n' + grokFeed : grokFeed;
1251
- }
1252
- else {
1253
- (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' No Grok feed reports available');
1254
- }
1255
- // Load Owner Directives (highest priority — always included)
1256
- const ownerDirectives = (0, engine_loaders_1.loadOwnerDirectives)();
1257
- if (ownerDirectives) {
1258
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, " Owner directives found — will include in CEO review (highest priority)");
1259
- cmoReports = ownerDirectives + (cmoReports ? "\n\n" + cmoReports : "");
1260
- }
1261
- // 5. CEO reviews CTO proposals + CMO reports
1262
- if (ctoReport.proposals.length > 0 || cmoReports) {
1263
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 4: CEO Reviews CTO Proposals + CMO Reports ---');
1264
- ctoDecisions = await this.ceo.reviewCTOProposals(ctoReport);
1265
- // Persist CEO decisions for CTO feedback loop + approved proposals tracking
1266
- (0, orchestrate_engine_1.persistCEODecisions)(ctoDecisions, ctoReport);
1267
- // Handle approved new_agent proposals
1268
- const agentCreator = new engine_agents_1.AgentCreator(this.spawnGate);
1269
- for (const proposal of ctoReport.proposals) {
1270
- if (proposal.category === 'new_agent' && proposal.agent_spec) {
1271
- // Check if CEO approved this specific proposal
1272
- if (ctoDecisions.includes(proposal.id) && ctoDecisions.toUpperCase().includes('APPROVED')) {
1273
- (0, engine_primitives_1.log)(engine_primitives_1.c.green, `\n 🤖 CEO approved new agent: ${proposal.agent_spec.name}`);
1274
- agentCreator.createAgent(proposal.agent_spec);
1275
- (0, engine_primitives_1.log)(engine_primitives_1.c.green, ` Agent will be loaded on next orchestrator run.`);
1276
- }
1277
- else {
1278
- (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` CEO did not approve agent: ${proposal.agent_spec.name}`);
1287
+ if (this.buildPath === 'regulated') {
1288
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, '\n--- Phase 3: CTO Data-Driven Analysis + CMO Reports ---');
1289
+ try {
1290
+ ctoReport = await this.cto.analyze();
1291
+ // Load CMO reports (produced independently by Manus AI runner)
1292
+ cmoReports = (0, engine_loaders_1.loadCMOReports)();
1293
+ if (cmoReports) {
1294
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ' CMO reports found — will include in CEO review');
1295
+ }
1296
+ else {
1297
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' No CMO reports available yet');
1298
+ }
1299
+ // Load CTO tech-watch reports (produced independently by run-cto-techwatch.ts)
1300
+ const ctoTechWatch = (0, engine_loaders_1.loadCTOTechWatchReports)();
1301
+ if (ctoTechWatch) {
1302
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' CTO tech-watch reports found — will include in CEO review');
1303
+ cmoReports = cmoReports ? cmoReports + '\n\n' + ctoTechWatch : ctoTechWatch;
1304
+ }
1305
+ else {
1306
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' No CTO tech-watch reports available yet');
1307
+ }
1308
+ // Load Grok intelligence feed (Grok AI monitors X/Twitter for OpenClaw news)
1309
+ const grokFeed = (0, engine_loaders_1.loadGrokFeed)();
1310
+ if (grokFeed) {
1311
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ' Grok intelligence feed found — will include in CEO review');
1312
+ cmoReports = cmoReports ? cmoReports + '\n\n' + grokFeed : grokFeed;
1313
+ }
1314
+ else {
1315
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' No Grok feed reports available');
1316
+ }
1317
+ // Load Owner Directives (highest priority — always included)
1318
+ const ownerDirectives = (0, engine_loaders_1.loadOwnerDirectives)();
1319
+ if (ownerDirectives) {
1320
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, " Owner directives found — will include in CEO review (highest priority)");
1321
+ cmoReports = ownerDirectives + (cmoReports ? "\n\n" + cmoReports : "");
1322
+ }
1323
+ // 5. CEO reviews CTO proposals + CMO reports
1324
+ if (ctoReport.proposals.length > 0 || cmoReports) {
1325
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 4: CEO Reviews CTO Proposals + CMO Reports ---');
1326
+ ctoDecisions = await this.ceo.reviewCTOProposals(ctoReport);
1327
+ // Persist CEO decisions for CTO feedback loop + approved proposals tracking
1328
+ (0, orchestrate_engine_1.persistCEODecisions)(ctoDecisions, ctoReport);
1329
+ // Handle approved new_agent proposals
1330
+ const agentCreator = new engine_agents_1.AgentCreator(this.spawnGate);
1331
+ for (const proposal of ctoReport.proposals) {
1332
+ if (proposal.category === 'new_agent' && proposal.agent_spec) {
1333
+ // Check if CEO approved this specific proposal
1334
+ if (ctoDecisions.includes(proposal.id) && ctoDecisions.toUpperCase().includes('APPROVED')) {
1335
+ (0, engine_primitives_1.log)(engine_primitives_1.c.green, `\n 🤖 CEO approved new agent: ${proposal.agent_spec.name}`);
1336
+ agentCreator.createAgent(proposal.agent_spec);
1337
+ (0, engine_primitives_1.log)(engine_primitives_1.c.green, ` Agent will be loaded on next orchestrator run.`);
1338
+ }
1339
+ else {
1340
+ (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` CEO did not approve agent: ${proposal.agent_spec.name}`);
1341
+ }
1279
1342
  }
1280
1343
  }
1281
1344
  }
1345
+ else {
1346
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' No proposals from CTO — stack is current and optimized');
1347
+ ctoDecisions = 'No proposals to review.';
1348
+ }
1282
1349
  }
1283
- else {
1284
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' No proposals from CTO stack is current and optimized');
1285
- ctoDecisions = 'No proposals to review.';
1350
+ catch (error) {
1351
+ (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` CTO/CEO review cycle skipped: ${error.message}`);
1352
+ ctoDecisions = 'CTO analysis was not performed this run.';
1286
1353
  }
1287
1354
  }
1288
- catch (error) {
1289
- (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` CTO/CEO review cycle skipped: ${error.message}`);
1290
- ctoDecisions = 'CTO analysis was not performed this run.';
1355
+ else {
1356
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, '\n--- Phases 3–4: CTO/CEO Governance Ceremony SKIPPED (fast track) ---');
1291
1357
  }
1292
1358
  // 6. CEO final assessment — B.14: only runs if ≥2 tasks rejected (skips if sprint went well)
1293
1359
  const rejectedCount = this.tasks.filter(t => t.status === 'rejected').length;
1294
1360
  (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 5: CEO Final Assessment ---');
1295
- if (rejectedCount >= 2) {
1361
+ if (this.buildPath === 'fast') {
1362
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, ' Fast track — skipping CEO final assessment');
1363
+ }
1364
+ else if (rejectedCount >= 2) {
1296
1365
  (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ` ${rejectedCount} tasks rejected — CEO reviewing...`);
1297
1366
  await this.ceo.reviewSprintProgress(this.tasks);
1298
1367
  }
@@ -1309,63 +1378,68 @@ ONLY output the JSON array. No markdown, no explanation.`;
1309
1378
  (0, event_bus_publisher_1.publishBudgetWarning)(_evtSprintId, _ws.burnPct, _ws.spentThisMonth, _ws.monthlyBudget).catch(() => { });
1310
1379
  }
1311
1380
  catch { /* wallet state unavailable */ }
1312
- // 6b. CTO autonomous post-sprint analysis (runs after EVERY sprint)
1313
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, '\n--- Phase 5b: CTO Post-Sprint Analysis (Autonomous) ---');
1381
+ // 6b. CTO autonomous post-sprint analysis regulated path only (TICKET-234)
1314
1382
  let postSprintReport = '';
1315
- try {
1316
- postSprintReport = await this.cto.postSprintAnalysis(this.tasks, this.stats);
1317
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' Post-sprint analysis saved to reports/cto/');
1318
- // Extract proposals from post-sprint analysis and feed into CEO review
1319
- const jsonMatch = postSprintReport.match(/```json\s*([\s\S]*?)```/);
1320
- if (jsonMatch) {
1321
- try {
1322
- const parsed = JSON.parse(jsonMatch[1].trim());
1323
- const postSprintProposals = parsed.proposals || [];
1324
- if (postSprintProposals.length > 0) {
1325
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ` Found ${postSprintProposals.length} proposals sending to CEO for autonomous review`);
1326
- // Build CTOReport for CEO review
1327
- const postSprintCTOReport = {
1328
- summary: parsed.summary || 'Post-sprint analysis proposals',
1329
- proposals: postSprintProposals.map((p) => ({
1330
- id: p.id,
1331
- title: p.title,
1332
- category: p.category,
1333
- description: p.description,
1334
- estimated_impact: p.estimated_impact || '',
1335
- risk_level: p.risk_level || 'medium',
1336
- implementation_steps: p.implementation_steps || [],
1337
- })),
1338
- metrics_reviewed: ['sprint_results', 'failure_patterns', 'trend_analysis'],
1339
- };
1340
- // Phase 5c: CEO autonomously reviews post-sprint proposals
1341
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 5c: CEO Reviews Post-Sprint Proposals (Autonomous) ---');
1342
- const postSprintDecisions = await this.ceo.reviewCTOProposals(postSprintCTOReport);
1343
- // Persist CEO decisions + update approved-proposals tracker
1344
- (0, orchestrate_engine_1.persistCEODecisions)(postSprintDecisions, postSprintCTOReport);
1345
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ' CEO post-sprint proposal review complete');
1383
+ if (this.buildPath === 'regulated') {
1384
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, '\n--- Phase 5b: CTO Post-Sprint Analysis (Autonomous) ---');
1385
+ try {
1386
+ postSprintReport = await this.cto.postSprintAnalysis(this.tasks, this.stats);
1387
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' Post-sprint analysis saved to reports/cto/');
1388
+ // Extract proposals from post-sprint analysis and feed into CEO review
1389
+ const jsonMatch = postSprintReport.match(/```json\s*([\s\S]*?)```/);
1390
+ if (jsonMatch) {
1391
+ try {
1392
+ const parsed = JSON.parse(jsonMatch[1].trim());
1393
+ const postSprintProposals = parsed.proposals || [];
1394
+ if (postSprintProposals.length > 0) {
1395
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ` Found ${postSprintProposals.length} proposals — sending to CEO for autonomous review`);
1396
+ // Build CTOReport for CEO review
1397
+ const postSprintCTOReport = {
1398
+ summary: parsed.summary || 'Post-sprint analysis proposals',
1399
+ proposals: postSprintProposals.map((p) => ({
1400
+ id: p.id,
1401
+ title: p.title,
1402
+ category: p.category,
1403
+ description: p.description,
1404
+ estimated_impact: p.estimated_impact || '',
1405
+ risk_level: p.risk_level || 'medium',
1406
+ implementation_steps: p.implementation_steps || [],
1407
+ })),
1408
+ metrics_reviewed: ['sprint_results', 'failure_patterns', 'trend_analysis'],
1409
+ };
1410
+ // Phase 5c: CEO autonomously reviews post-sprint proposals
1411
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 5c: CEO Reviews Post-Sprint Proposals (Autonomous) ---');
1412
+ const postSprintDecisions = await this.ceo.reviewCTOProposals(postSprintCTOReport);
1413
+ // Persist CEO decisions + update approved-proposals tracker
1414
+ (0, orchestrate_engine_1.persistCEODecisions)(postSprintDecisions, postSprintCTOReport);
1415
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, ' CEO post-sprint proposal review complete');
1416
+ }
1417
+ else {
1418
+ (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' No proposals in post-sprint analysis');
1419
+ }
1346
1420
  }
1347
- else {
1348
- (0, engine_primitives_1.log)(engine_primitives_1.c.cyan, ' No proposals in post-sprint analysis');
1421
+ catch (parseErr) {
1422
+ (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` Could not parse post-sprint proposals JSON: ${parseErr.message}`);
1349
1423
  }
1350
1424
  }
1351
- catch (parseErr) {
1352
- (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` Could not parse post-sprint proposals JSON: ${parseErr.message}`);
1353
- }
1354
1425
  }
1426
+ catch (error) {
1427
+ (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` Post-sprint analysis skipped: ${error.message}`);
1428
+ }
1429
+ // 7. CEO generates daily report
1430
+ (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 6: Daily Report Generation ---');
1431
+ const ctoReportStr = `Summary: ${ctoReport.summary}\nProposals: ${ctoReport.proposals.length}\n${ctoReport.proposals.map(p => `- [${p.category}] ${p.title} (${p.risk_level})`).join('\n')}`;
1432
+ const grokSection = (0, engine_loaders_1.loadGrokFeed)();
1433
+ const cmoSection = cmoReports
1434
+ ? '\n\n## CMO Activity (Manus AI)\n' + cmoReports.substring(0, 2000)
1435
+ : '\n\nCMO: No reports available this cycle.';
1436
+ const grokForReport = grokSection ? '\n\n## Grok Intelligence Feed\nGrok AI reports available — included in CTO/CEO analysis.' : '\n\nGrok: No feed reports this cycle.';
1437
+ const postSprintSection = postSprintReport ? '\n\n## CTO Post-Sprint Analysis\n' + postSprintReport.substring(0, 2000) : '';
1438
+ await this.ceo.generateDailyReport(this.tasks, this.stats, ctoReportStr + cmoSection + grokForReport + postSprintSection, ctoDecisions);
1355
1439
  }
1356
- catch (error) {
1357
- (0, engine_primitives_1.log)(engine_primitives_1.c.yellow, ` Post-sprint analysis skipped: ${error.message}`);
1440
+ else {
1441
+ (0, engine_primitives_1.log)(engine_primitives_1.c.gray, '\n--- Phases 5b–6: Post-Sprint Ceremony + Daily Report — SKIPPED (fast track) ---');
1358
1442
  }
1359
- // 7. CEO generates daily report
1360
- (0, engine_primitives_1.log)(engine_primitives_1.c.magenta, '\n--- Phase 6: Daily Report Generation ---');
1361
- const ctoReportStr = `Summary: ${ctoReport.summary}\nProposals: ${ctoReport.proposals.length}\n${ctoReport.proposals.map(p => `- [${p.category}] ${p.title} (${p.risk_level})`).join('\n')}`;
1362
- const grokSection = (0, engine_loaders_1.loadGrokFeed)();
1363
- const cmoSection = cmoReports
1364
- ? '\n\n## CMO Activity (Manus AI)\n' + cmoReports.substring(0, 2000)
1365
- : '\n\nCMO: No reports available this cycle.';
1366
- const grokForReport = grokSection ? '\n\n## Grok Intelligence Feed\nGrok AI reports available — included in CTO/CEO analysis.' : '\n\nGrok: No feed reports this cycle.';
1367
- const postSprintSection = postSprintReport ? '\n\n## CTO Post-Sprint Analysis\n' + postSprintReport.substring(0, 2000) : '';
1368
- await this.ceo.generateDailyReport(this.tasks, this.stats, ctoReportStr + cmoSection + grokForReport + postSprintSection, ctoDecisions);
1369
1443
  // 8. Save updated sprint state
1370
1444
  const sprintFile = process.argv[2] || 'sprints/current.json';
1371
1445
  (0, fs_1.writeFileSync)(sprintFile, JSON.stringify({ tasks: this.tasks }, null, 2));
@@ -1412,6 +1486,8 @@ ONLY output the JSON array. No markdown, no explanation.`;
1412
1486
  git_head_before: gitHeadBefore,
1413
1487
  git_head_after: gitHeadAfter,
1414
1488
  sovereign_mode: engine_primitives_1.SOVEREIGN_MODE,
1489
+ build_path: this.buildPath, // TICKET-234: 'regulated' | 'fast'
1490
+ triage: this.triage, // TICKET-234: classifier reason + triggers
1415
1491
  summary: {
1416
1492
  total_tasks: this.tasks.length,
1417
1493
  done: this.tasks.filter(t => t.status === 'done').length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kognai/orchestrator-core",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Kognai sovereign orchestrator — core engine (template-agnostic). Shared by all products (Kognai/coding, Voxight/market-intel, Invoica/fin-compliance); each supplies only its template. Replaces per-repo forks of orchestrate-agents-v2 / sprint-runner / lib.",
5
5
  "license": "MIT",
6
6
  "author": "SkinGem",