mister-wolf 2.3.2 → 2.5.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/dist/adapters/cli/cli-entry.d.ts.map +1 -1
- package/dist/adapters/cli/cli-entry.js +2 -0
- package/dist/adapters/cli/cli-entry.js.map +1 -1
- package/dist/adapters/cli/commands/analytics.d.ts +7 -2
- package/dist/adapters/cli/commands/analytics.d.ts.map +1 -1
- package/dist/adapters/cli/commands/analytics.js +83 -24
- package/dist/adapters/cli/commands/analytics.js.map +1 -1
- package/dist/adapters/cli/commands/dashboard.d.ts +5 -0
- package/dist/adapters/cli/commands/dashboard.d.ts.map +1 -1
- package/dist/adapters/cli/commands/dashboard.js +41 -19
- package/dist/adapters/cli/commands/dashboard.js.map +1 -1
- package/dist/adapters/cli/commands/memory-effectiveness.d.ts.map +1 -1
- package/dist/adapters/cli/commands/memory-effectiveness.js +3 -3
- package/dist/adapters/cli/commands/memory-effectiveness.js.map +1 -1
- package/dist/adapters/cli/commands/table-render.d.ts +0 -3
- package/dist/adapters/cli/commands/table-render.d.ts.map +1 -1
- package/dist/adapters/cli/commands/table-render.js +0 -9
- package/dist/adapters/cli/commands/table-render.js.map +1 -1
- package/dist/adapters/cli/commands/task-eval.d.ts +3 -0
- package/dist/adapters/cli/commands/task-eval.d.ts.map +1 -0
- package/dist/adapters/cli/commands/task-eval.js +38 -0
- package/dist/adapters/cli/commands/task-eval.js.map +1 -0
- package/dist/adapters/fs/session-metrics-log.d.ts +75 -41
- package/dist/adapters/fs/session-metrics-log.d.ts.map +1 -1
- package/dist/adapters/fs/session-metrics-log.js +90 -9
- package/dist/adapters/fs/session-metrics-log.js.map +1 -1
- package/dist/adapters/mcp/mcp-schemas.d.ts +2 -1
- package/dist/adapters/mcp/mcp-schemas.d.ts.map +1 -1
- package/dist/adapters/mcp/mcp-schemas.js +14 -1
- package/dist/adapters/mcp/mcp-schemas.js.map +1 -1
- package/dist/adapters/mcp/mcp-tools.d.ts.map +1 -1
- package/dist/adapters/mcp/mcp-tools.js +7 -4
- package/dist/adapters/mcp/mcp-tools.js.map +1 -1
- package/dist/app/use-cases/build-analytics.d.ts +78 -9
- package/dist/app/use-cases/build-analytics.d.ts.map +1 -1
- package/dist/app/use-cases/build-analytics.js +212 -19
- package/dist/app/use-cases/build-analytics.js.map +1 -1
- package/dist/app/use-cases/build-dashboard.d.ts +5 -0
- package/dist/app/use-cases/build-dashboard.d.ts.map +1 -1
- package/dist/app/use-cases/build-dashboard.js +2 -1
- package/dist/app/use-cases/build-dashboard.js.map +1 -1
- package/dist/app/use-cases/effectiveness.d.ts +4 -4
- package/dist/app/use-cases/effectiveness.d.ts.map +1 -1
- package/dist/app/use-cases/effectiveness.js +7 -7
- package/dist/app/use-cases/effectiveness.js.map +1 -1
- package/dist/app/use-cases/snapshot-delta.js +3 -3
- package/dist/app/use-cases/snapshot-delta.js.map +1 -1
- package/dist/app/use-cases/tally-council-votes.d.ts +2 -0
- package/dist/app/use-cases/tally-council-votes.d.ts.map +1 -1
- package/dist/app/use-cases/tally-council-votes.js +1 -1
- package/dist/app/use-cases/tally-council-votes.js.map +1 -1
- package/package.json +1 -1
- package/templates/base/agents/worker-implementer.md +2 -0
- package/templates/base/agents/worker-researcher.md +2 -0
- package/templates/base/agents/worker-reviewer.md +2 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* M5 (ядро L2): `wolf analytics` — реестры и
|
|
2
|
+
* M5 (ядро L2): `wolf analytics` — реестры и недельная активность (Q1–Q4, Q6, Q8, Q10–Q12).
|
|
3
3
|
* Чистая детерминированная агрегация store + signals + event-log + run-log, без LLM.
|
|
4
4
|
* Дизайн: docs/superpowers/specs/2026-09-03-analytics-metrics-dashboard-design.md §5 M5, §6.2.
|
|
5
5
|
*/
|
|
6
6
|
import type { MemoryStore } from '../../ports/memory-store.port.js';
|
|
7
7
|
import type { EventLog } from '../../ports/event-log.port.js';
|
|
8
|
+
import type { RelationLog } from '../../ports/relation-log.port.js';
|
|
8
9
|
import type { Clock } from '../../ports/clock.port.js';
|
|
9
10
|
import { type SignalEvent } from '../../adapters/fs/session-metrics-log.js';
|
|
10
11
|
import type { PricingTable } from '../../domain/pricing.js';
|
|
@@ -60,7 +61,7 @@ export interface RuleRankingRow {
|
|
|
60
61
|
checked: number | null;
|
|
61
62
|
silent: boolean;
|
|
62
63
|
}
|
|
63
|
-
export interface
|
|
64
|
+
export interface WeeklyActivityWeek {
|
|
64
65
|
week: string;
|
|
65
66
|
writes: number;
|
|
66
67
|
delivers: number;
|
|
@@ -80,17 +81,34 @@ export interface OutlierRun {
|
|
|
80
81
|
export interface AgentLedgerRow {
|
|
81
82
|
agent: string;
|
|
82
83
|
runs: number;
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
processFailures: number;
|
|
85
|
+
processFailureRatePct: number | null;
|
|
85
86
|
weighted: number;
|
|
86
87
|
avgDurationMs: number | null;
|
|
87
88
|
costUsd: number | null;
|
|
88
89
|
toolErrors: number;
|
|
89
90
|
complaintsBy: number;
|
|
90
91
|
complaintsAbout: number;
|
|
91
|
-
|
|
92
|
+
completedRuns: number;
|
|
93
|
+
accepted: number;
|
|
92
94
|
holdoutPrevented: number | null;
|
|
93
95
|
}
|
|
96
|
+
/** D4: accepted-вердикты по строгой session-связке (link ненадёжен до P1). */
|
|
97
|
+
export interface AcceptanceStats {
|
|
98
|
+
accepted: number;
|
|
99
|
+
costPerAcceptedTask: number | null;
|
|
100
|
+
}
|
|
101
|
+
/** D5: coverage — scored-вердикты / run-сигналы (интерим-знаменатель, честный — P1). */
|
|
102
|
+
export interface CoverageStats {
|
|
103
|
+
scored: number;
|
|
104
|
+
runs: number;
|
|
105
|
+
scoredTaskRatePct: number | null;
|
|
106
|
+
}
|
|
107
|
+
/** D7: data-quality сигнального лога (битые строки не роняют контур — только метрика). */
|
|
108
|
+
export interface DataQualityStats {
|
|
109
|
+
validEventRatePct: number | null;
|
|
110
|
+
malformedLines: number;
|
|
111
|
+
}
|
|
94
112
|
export interface StewardView {
|
|
95
113
|
mutations: {
|
|
96
114
|
kind: string;
|
|
@@ -124,6 +142,44 @@ export interface ExperimentReadiness {
|
|
|
124
142
|
runs: number;
|
|
125
143
|
}[];
|
|
126
144
|
}
|
|
145
|
+
export interface CouncilWeekActivity {
|
|
146
|
+
week: string;
|
|
147
|
+
questions: number;
|
|
148
|
+
opinions: number;
|
|
149
|
+
syntheses: number;
|
|
150
|
+
}
|
|
151
|
+
export interface CouncilOpenQuestion {
|
|
152
|
+
id: string;
|
|
153
|
+
title: string;
|
|
154
|
+
daysOpen: number;
|
|
155
|
+
opinions: number;
|
|
156
|
+
votes: Record<string, number>;
|
|
157
|
+
}
|
|
158
|
+
export interface CouncilsView {
|
|
159
|
+
questions: {
|
|
160
|
+
total: number;
|
|
161
|
+
inWindow: number;
|
|
162
|
+
open: number;
|
|
163
|
+
};
|
|
164
|
+
opinions: {
|
|
165
|
+
total: number;
|
|
166
|
+
perQuestionMin: number | null;
|
|
167
|
+
perQuestionAvg: number | null;
|
|
168
|
+
perQuestionMax: number | null;
|
|
169
|
+
};
|
|
170
|
+
participation: {
|
|
171
|
+
agent: string;
|
|
172
|
+
opinions: number;
|
|
173
|
+
}[];
|
|
174
|
+
votes: Record<string, number>;
|
|
175
|
+
synthesis: {
|
|
176
|
+
questionsWithSynthesis: number;
|
|
177
|
+
sharePct: number | null;
|
|
178
|
+
medianHours: number | null;
|
|
179
|
+
};
|
|
180
|
+
weeks: CouncilWeekActivity[];
|
|
181
|
+
openQuestions: CouncilOpenQuestion[];
|
|
182
|
+
}
|
|
127
183
|
export interface AnalyticsReport {
|
|
128
184
|
generatedAt: string;
|
|
129
185
|
thresholds: LifecycleThresholds;
|
|
@@ -133,15 +189,20 @@ export interface AnalyticsReport {
|
|
|
133
189
|
};
|
|
134
190
|
tools: ToolLedgerRow[];
|
|
135
191
|
rules: RuleRankingRow[];
|
|
136
|
-
|
|
192
|
+
weeklyActivity: WeeklyActivityWeek[];
|
|
137
193
|
outliers: OutlierRun[];
|
|
138
194
|
agents: AgentLedgerRow[];
|
|
139
195
|
steward: StewardView;
|
|
140
196
|
readiness: ExperimentReadiness;
|
|
197
|
+
councils: CouncilsView;
|
|
198
|
+
acceptance: AcceptanceStats;
|
|
199
|
+
coverage: CoverageStats;
|
|
200
|
+
dataQuality: DataQualityStats;
|
|
141
201
|
}
|
|
142
202
|
export interface AnalyticsDeps {
|
|
143
203
|
store: MemoryStore;
|
|
144
204
|
log: EventLog;
|
|
205
|
+
relations: RelationLog;
|
|
145
206
|
clock: Clock;
|
|
146
207
|
}
|
|
147
208
|
export interface AnalyticsInput {
|
|
@@ -152,6 +213,11 @@ export interface AnalyticsInput {
|
|
|
152
213
|
weeks?: number;
|
|
153
214
|
topOutliers?: number;
|
|
154
215
|
pricing?: PricingTable;
|
|
216
|
+
/** D7: счётчики readSignalLog — источник dataQuality (undefined → n/a). */
|
|
217
|
+
signalLogStats?: {
|
|
218
|
+
malformedLines: number;
|
|
219
|
+
totalLines: number;
|
|
220
|
+
};
|
|
155
221
|
}
|
|
156
222
|
/**
|
|
157
223
|
* Полный аналитический отчёт M5 из трёх портов: чистая агрегация, детерминированная.
|
|
@@ -159,7 +225,7 @@ export interface AnalyticsInput {
|
|
|
159
225
|
*/
|
|
160
226
|
export declare function buildAnalyticsReport(deps: AnalyticsDeps, input: AnalyticsInput): Promise<AnalyticsReport>;
|
|
161
227
|
export interface AnalyticsViewFilter {
|
|
162
|
-
view: 'memory' | 'tools' | 'rules' | '
|
|
228
|
+
view: 'memory' | 'tools' | 'rules' | 'weeklyActivity' | 'agents' | 'steward' | 'outliers' | 'readiness' | 'councils' | 'all';
|
|
163
229
|
class?: 'new' | 'sleeper' | 'workhorse' | 'dead';
|
|
164
230
|
type?: string;
|
|
165
231
|
origin?: 'script' | 'model-native';
|
|
@@ -178,8 +244,8 @@ export type AnalyticsViewPayload = {
|
|
|
178
244
|
view: 'rules';
|
|
179
245
|
rows: RuleRankingRow[];
|
|
180
246
|
} | {
|
|
181
|
-
view: '
|
|
182
|
-
weeks:
|
|
247
|
+
view: 'weeklyActivity';
|
|
248
|
+
weeks: WeeklyActivityWeek[];
|
|
183
249
|
} | {
|
|
184
250
|
view: 'agents';
|
|
185
251
|
rows: AgentLedgerRow[];
|
|
@@ -192,6 +258,9 @@ export type AnalyticsViewPayload = {
|
|
|
192
258
|
} | {
|
|
193
259
|
view: 'readiness';
|
|
194
260
|
readiness: ExperimentReadiness;
|
|
261
|
+
} | {
|
|
262
|
+
view: 'councils';
|
|
263
|
+
councils: CouncilsView;
|
|
195
264
|
} | {
|
|
196
265
|
view: 'all';
|
|
197
266
|
report: AnalyticsReport;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-analytics.d.ts","sourceRoot":"","sources":["../../../src/app/use-cases/build-analytics.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAGvD,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,0CAA0C,CAAC;AAIvG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"build-analytics.d.ts","sourceRoot":"","sources":["../../../src/app/use-cases/build-analytics.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import { UNCATEGORIZED_ERROR_CLASS } from '../../domain/error-class.js';
|
|
|
4
4
|
import { runCostUsd } from '../../domain/pricing.js';
|
|
5
5
|
import { silentRuleIds } from './learn-decay.js';
|
|
6
6
|
import { mondayOf } from './generate-insights.js';
|
|
7
|
+
import { extractVote } from './tally-council-votes.js';
|
|
7
8
|
export const DEFAULT_LIFECYCLE_THRESHOLDS = { newDays: 14, workhorseUses: 3 };
|
|
8
9
|
/** Override из config (analytics.thresholds) поверх дефолтов (D7). */
|
|
9
10
|
export function resolveLifecycleThresholds(override) {
|
|
@@ -201,9 +202,10 @@ function weekBuckets(now, weeks) {
|
|
|
201
202
|
keys.push(new Date(currentMondayMs - i * 7 * 86_400_000).toISOString().slice(0, 10));
|
|
202
203
|
return keys;
|
|
203
204
|
}
|
|
204
|
-
/**
|
|
205
|
-
* prevented НЕ входит: holdout-счётчики кумулятивны, без
|
|
206
|
-
|
|
205
|
+
/** Недельная активность Q6: write (memory.added) → deliver (delivery-сигналы) → trigger
|
|
206
|
+
* (уникальные имена). prevented НЕ входит: holdout-счётчики кумулятивны, без
|
|
207
|
+
* таймстампов (спека §5 M5, rev.4). */
|
|
208
|
+
function buildWeeklyActivity(events, signals, now, weeks) {
|
|
207
209
|
const buckets = new Map();
|
|
208
210
|
for (const week of weekBuckets(now, weeks)) {
|
|
209
211
|
buckets.set(week, { week, writes: 0, delivers: 0, names: new Set() });
|
|
@@ -250,10 +252,58 @@ function buildOutliers(runLogText, pricing, top) {
|
|
|
250
252
|
tools: e.tools ?? [],
|
|
251
253
|
}));
|
|
252
254
|
}
|
|
255
|
+
/** Acceptance D4: accepted-вердикт считается ТОЛЬКО при строгой session-связке —
|
|
256
|
+
* session_id !== null и существует ≥1 run-сигнал с тем же session_id (без связки
|
|
257
|
+
* вердикт не атрибутируется: link ненадёжен до P1). costPerAcceptedTask = сумма
|
|
258
|
+
* weighted linked-ранов / число вердиктов. Атрибуция: каждому агенту множества
|
|
259
|
+
* gen_ai.agent linked-ранов вердикта +1 (дедуп внутри одного вердикта). */
|
|
260
|
+
function buildAcceptance(signals) {
|
|
261
|
+
const runsBySession = new Map();
|
|
262
|
+
for (const s of signals) {
|
|
263
|
+
if (s.event !== 'run' || s.session_id === null)
|
|
264
|
+
continue;
|
|
265
|
+
const arr = runsBySession.get(s.session_id) ?? [];
|
|
266
|
+
arr.push(s);
|
|
267
|
+
runsBySession.set(s.session_id, arr);
|
|
268
|
+
}
|
|
269
|
+
const acceptedByAgent = new Map();
|
|
270
|
+
const acceptedSessions = new Set();
|
|
271
|
+
let accepted = 0;
|
|
272
|
+
for (const s of signals) {
|
|
273
|
+
if (s.event !== 'task_evaluated' || s.detail?.verdict !== 'accepted')
|
|
274
|
+
continue;
|
|
275
|
+
// strict-link: без session_id или без run-связки вердикт НЕ считается
|
|
276
|
+
if (s.session_id === null || !runsBySession.has(s.session_id))
|
|
277
|
+
continue;
|
|
278
|
+
accepted += 1;
|
|
279
|
+
acceptedSessions.add(s.session_id);
|
|
280
|
+
const agents = new Set();
|
|
281
|
+
for (const r of runsBySession.get(s.session_id)) {
|
|
282
|
+
if (typeof r.gen_ai.agent === 'string' && r.gen_ai.agent !== '')
|
|
283
|
+
agents.add(r.gen_ai.agent);
|
|
284
|
+
}
|
|
285
|
+
for (const a of agents)
|
|
286
|
+
acceptedByAgent.set(a, (acceptedByAgent.get(a) ?? 0) + 1);
|
|
287
|
+
}
|
|
288
|
+
let linkedWeighted = 0;
|
|
289
|
+
for (const [session, runs] of runsBySession) {
|
|
290
|
+
if (!acceptedSessions.has(session))
|
|
291
|
+
continue;
|
|
292
|
+
for (const r of runs)
|
|
293
|
+
linkedWeighted += finiteNumber(r.weighted) ?? 0;
|
|
294
|
+
}
|
|
295
|
+
return { accepted, costPerAcceptedTask: accepted > 0 ? linkedWeighted / accepted : null, acceptedByAgent };
|
|
296
|
+
}
|
|
297
|
+
/** Coverage D5: scored = task_evaluated (любой verdict), runs = run-сигналы. */
|
|
298
|
+
function buildCoverage(signals) {
|
|
299
|
+
const scored = signals.filter((s) => s.event === 'task_evaluated').length;
|
|
300
|
+
const runs = signals.filter((s) => s.event === 'run').length;
|
|
301
|
+
return { scored, runs, scoredTaskRatePct: runs > 0 ? (scored / runs) * 100 : null };
|
|
302
|
+
}
|
|
253
303
|
/** Agent ledger Q11: строки по run-агентам ∪ complaint-акторам `agent:<имя>`; три уровня —
|
|
254
|
-
* объём (runs/weighted/duration/cost), проблемы (
|
|
255
|
-
* (
|
|
256
|
-
function buildAgents(signals, objects, pricing) {
|
|
304
|
+
* объём (runs/weighted/duration/cost), проблемы (processFailures/toolErrors/жалобы), достижения
|
|
305
|
+
* (completedRuns/accepted/holdout_prevented его rule/lesson). */
|
|
306
|
+
function buildAgents(signals, objects, pricing, acceptedByAgent) {
|
|
257
307
|
const AGENT_PREFIX = 'agent:';
|
|
258
308
|
const acc = new Map();
|
|
259
309
|
const rowOf = (name) => {
|
|
@@ -261,8 +311,9 @@ function buildAgents(signals, objects, pricing) {
|
|
|
261
311
|
if (r === undefined) {
|
|
262
312
|
r = {
|
|
263
313
|
runs: 0,
|
|
264
|
-
|
|
265
|
-
|
|
314
|
+
processFailures: 0,
|
|
315
|
+
completedRuns: 0,
|
|
316
|
+
accepted: 0,
|
|
266
317
|
weighted: 0,
|
|
267
318
|
durations: [],
|
|
268
319
|
cost: null,
|
|
@@ -282,9 +333,9 @@ function buildAgents(signals, objects, pricing) {
|
|
|
282
333
|
const r = rowOf(ev.gen_ai.agent);
|
|
283
334
|
r.runs += 1;
|
|
284
335
|
if (ev.outcome === 'ok')
|
|
285
|
-
r.
|
|
336
|
+
r.completedRuns += 1;
|
|
286
337
|
else
|
|
287
|
-
r.
|
|
338
|
+
r.processFailures += 1;
|
|
288
339
|
r.weighted += finiteNumber(ev.weighted) ?? 0;
|
|
289
340
|
const d = finiteNumber(ev.duration_ms);
|
|
290
341
|
if (d !== null)
|
|
@@ -334,19 +385,23 @@ function buildAgents(signals, objects, pricing) {
|
|
|
334
385
|
acc.get(name).hasHoldout = true;
|
|
335
386
|
}
|
|
336
387
|
}
|
|
388
|
+
// D4: accepted-атрибуция — строки linked-ранов уже существуют (раны их создали)
|
|
389
|
+
for (const [name, n] of acceptedByAgent)
|
|
390
|
+
rowOf(name).accepted += n;
|
|
337
391
|
return [...acc.entries()]
|
|
338
392
|
.map(([agent, r]) => ({
|
|
339
393
|
agent,
|
|
340
394
|
runs: r.runs,
|
|
341
|
-
|
|
342
|
-
|
|
395
|
+
processFailures: r.processFailures,
|
|
396
|
+
processFailureRatePct: r.runs > 0 ? (r.processFailures / r.runs) * 100 : null,
|
|
343
397
|
weighted: r.weighted,
|
|
344
398
|
avgDurationMs: r.durations.length > 0 ? r.durations.reduce((s, v) => s + v, 0) / r.durations.length : null,
|
|
345
399
|
costUsd: r.cost,
|
|
346
400
|
toolErrors: r.toolErrors,
|
|
347
401
|
complaintsBy: r.complaintsBy,
|
|
348
402
|
complaintsAbout: r.complaintsAbout,
|
|
349
|
-
|
|
403
|
+
completedRuns: r.completedRuns,
|
|
404
|
+
accepted: r.accepted,
|
|
350
405
|
holdoutPrevented: r.hasHoldout ? r.holdoutSum : null,
|
|
351
406
|
}))
|
|
352
407
|
.sort((a, b) => b.runs - a.runs || a.agent.localeCompare(b.agent));
|
|
@@ -370,7 +425,7 @@ function mutationKindOf(ev) {
|
|
|
370
425
|
}
|
|
371
426
|
}
|
|
372
427
|
const MUTATION_KINDS = ['update', 'supersede', 'resolve', 'transition', 'tool-mutation'];
|
|
373
|
-
/** Steward view Q12: мутации за окно weeks (то же, что
|
|
428
|
+
/** Steward view Q12: мутации за окно weeks (то же, что weekly activity), жалобная воронка,
|
|
374
429
|
* SLA-эскалации (dispatch_ages >= 3), рецидивы, churn, доля авто-мутаций. */
|
|
375
430
|
function buildSteward(events, signals, objects, now, weeks) {
|
|
376
431
|
const keys = weekBuckets(now, weeks);
|
|
@@ -518,6 +573,124 @@ function buildReadiness(signals) {
|
|
|
518
573
|
// ---------------------------------------------------------------------------
|
|
519
574
|
// Use-case
|
|
520
575
|
// ---------------------------------------------------------------------------
|
|
576
|
+
/** Консилиумы: вопросы/мнения/синтезы из councils-subdir + relations answers/based_on.
|
|
577
|
+
* Чистая агрегация по уже выгруженным объектам; ровно два вызова relations.list
|
|
578
|
+
* (predicate answers и based_on — без N+1 по вопросам). Голоса — extractVote
|
|
579
|
+
* из tally-council-votes (единственный парсер). */
|
|
580
|
+
async function buildCouncils(objects, relations, now, weeks) {
|
|
581
|
+
const byId = new Map(objects.map((o) => [o.id, o]));
|
|
582
|
+
const questions = objects.filter((o) => o.type === 'council-question');
|
|
583
|
+
const opinions = objects.filter((o) => o.type === 'council-opinion');
|
|
584
|
+
const syntheses = objects.filter((o) => o.type === 'synthesis');
|
|
585
|
+
// вопрос → id его мнений; субъект-реляция засчитывается только если это
|
|
586
|
+
// существующее в store мнение (прецедент tallyCouncilVotes)
|
|
587
|
+
const answersByQuestion = new Map();
|
|
588
|
+
for (const r of await relations.list({ predicate: 'answers' })) {
|
|
589
|
+
const op = byId.get(r.subject);
|
|
590
|
+
if (op === undefined || op.type !== 'council-opinion')
|
|
591
|
+
continue;
|
|
592
|
+
const arr = answersByQuestion.get(r.object) ?? [];
|
|
593
|
+
arr.push(op.id);
|
|
594
|
+
answersByQuestion.set(r.object, arr);
|
|
595
|
+
}
|
|
596
|
+
// недельные бакеты (те же ключи, что weekly activity) по mondayOf(created_at)
|
|
597
|
+
const buckets = new Map(weekBuckets(now, weeks).map((week) => [week, { week, questions: 0, opinions: 0, syntheses: 0 }]));
|
|
598
|
+
const bump = (o, key) => {
|
|
599
|
+
const b = buckets.get(mondayOf(o.created_at));
|
|
600
|
+
if (b !== undefined)
|
|
601
|
+
b[key] += 1;
|
|
602
|
+
};
|
|
603
|
+
for (const q of questions)
|
|
604
|
+
bump(q, 'questions');
|
|
605
|
+
for (const op of opinions)
|
|
606
|
+
bump(op, 'opinions');
|
|
607
|
+
for (const s of syntheses)
|
|
608
|
+
bump(s, 'syntheses');
|
|
609
|
+
const weeksOut = [...buckets.values()];
|
|
610
|
+
const inWindow = weeksOut.reduce((sum, w) => sum + w.questions, 0);
|
|
611
|
+
// per-question статистика по ВСЕМ вопросам (вопрос без мнений — 0)
|
|
612
|
+
const counts = questions.map((q) => (answersByQuestion.get(q.id) ?? []).length);
|
|
613
|
+
// участие: мнения по created_by (voter = created_by, прецедент CouncilTally)
|
|
614
|
+
const partAcc = new Map();
|
|
615
|
+
for (const op of opinions)
|
|
616
|
+
partAcc.set(op.created_by, (partAcc.get(op.created_by) ?? 0) + 1);
|
|
617
|
+
const participation = [...partAcc.entries()]
|
|
618
|
+
.map(([agent, n]) => ({ agent, opinions: n }))
|
|
619
|
+
.sort((a, b) => b.opinions - a.opinions || a.agent.localeCompare(b.agent));
|
|
620
|
+
// голоса: единый парсер extractVote (поле vote → body VOTE: → TIMEOUT)
|
|
621
|
+
const tally = (ops) => {
|
|
622
|
+
const v = {};
|
|
623
|
+
for (const opId of ops) {
|
|
624
|
+
const vote = extractVote(byId.get(opId));
|
|
625
|
+
v[vote] = (v[vote] ?? 0) + 1;
|
|
626
|
+
}
|
|
627
|
+
return v;
|
|
628
|
+
};
|
|
629
|
+
const votes = tally(opinions.map((o) => o.id));
|
|
630
|
+
// синтез: вопрос «с синтезом» = ∃ синтез с based_on → мнение этого вопроса;
|
|
631
|
+
// берём самый ранний подходящий синтез (медиана времени до синтеза)
|
|
632
|
+
const questionByOpinionId = new Map();
|
|
633
|
+
for (const [qid, opIds] of answersByQuestion) {
|
|
634
|
+
for (const opId of opIds)
|
|
635
|
+
questionByOpinionId.set(opId, qid);
|
|
636
|
+
}
|
|
637
|
+
const earliestSynByQuestion = new Map();
|
|
638
|
+
for (const r of await relations.list({ predicate: 'based_on' })) {
|
|
639
|
+
const syn = byId.get(r.subject);
|
|
640
|
+
if (syn === undefined || syn.type !== 'synthesis')
|
|
641
|
+
continue;
|
|
642
|
+
const qid = questionByOpinionId.get(r.object);
|
|
643
|
+
if (qid === undefined)
|
|
644
|
+
continue;
|
|
645
|
+
const cur = earliestSynByQuestion.get(qid);
|
|
646
|
+
if (cur === undefined || syn.created_at < cur)
|
|
647
|
+
earliestSynByQuestion.set(qid, syn.created_at);
|
|
648
|
+
}
|
|
649
|
+
const questionsWithSynthesis = earliestSynByQuestion.size;
|
|
650
|
+
const hours = [...earliestSynByQuestion].map(([qid, synTs]) => (Date.parse(synTs) - Date.parse(byId.get(qid).created_at)) / 3_600_000);
|
|
651
|
+
hours.sort((a, b) => a - b);
|
|
652
|
+
const medianHours = hours.length === 0
|
|
653
|
+
? null
|
|
654
|
+
: hours.length % 2 === 1
|
|
655
|
+
? hours[(hours.length - 1) / 2]
|
|
656
|
+
: (hours[hours.length / 2 - 1] + hours[hours.length / 2]) / 2;
|
|
657
|
+
// открытые вопросы: возраст, число мнений, расклад голосов
|
|
658
|
+
const openQuestions = questions
|
|
659
|
+
.filter((q) => q.status === 'open')
|
|
660
|
+
.map((q) => {
|
|
661
|
+
const opIds = answersByQuestion.get(q.id) ?? [];
|
|
662
|
+
return {
|
|
663
|
+
id: q.id,
|
|
664
|
+
title: q.title,
|
|
665
|
+
daysOpen: Math.floor((now.getTime() - Date.parse(q.created_at)) / 86_400_000),
|
|
666
|
+
opinions: opIds.length,
|
|
667
|
+
votes: tally(opIds),
|
|
668
|
+
};
|
|
669
|
+
})
|
|
670
|
+
.sort((a, b) => b.daysOpen - a.daysOpen || a.id.localeCompare(b.id));
|
|
671
|
+
return {
|
|
672
|
+
questions: {
|
|
673
|
+
total: questions.length,
|
|
674
|
+
inWindow,
|
|
675
|
+
open: questions.filter((q) => q.status === 'open').length,
|
|
676
|
+
},
|
|
677
|
+
opinions: {
|
|
678
|
+
total: opinions.length,
|
|
679
|
+
perQuestionMin: questions.length > 0 ? Math.min(...counts) : null,
|
|
680
|
+
perQuestionAvg: questions.length > 0 ? counts.reduce((s, v) => s + v, 0) / counts.length : null,
|
|
681
|
+
perQuestionMax: questions.length > 0 ? Math.max(...counts) : null,
|
|
682
|
+
},
|
|
683
|
+
participation,
|
|
684
|
+
votes,
|
|
685
|
+
synthesis: {
|
|
686
|
+
questionsWithSynthesis,
|
|
687
|
+
sharePct: questions.length > 0 ? (questionsWithSynthesis / questions.length) * 100 : null,
|
|
688
|
+
medianHours,
|
|
689
|
+
},
|
|
690
|
+
weeks: weeksOut,
|
|
691
|
+
openQuestions,
|
|
692
|
+
};
|
|
693
|
+
}
|
|
521
694
|
/**
|
|
522
695
|
* Полный аналитический отчёт M5 из трёх портов: чистая агрегация, детерминированная.
|
|
523
696
|
* Не падает на пустой памяти — все блоки возвращают нули/null/пустые массивы.
|
|
@@ -532,22 +705,40 @@ export async function buildAnalyticsReport(deps, input) {
|
|
|
532
705
|
const memory = buildMemoryLedger(allObjects, events, input.signals, now, thresholds);
|
|
533
706
|
const tools = buildToolLedger(allObjects.filter((o) => o.type === 'tool'), input.signals, input.runLogText, input.patternThreshold ?? DEFAULT_PATTERN_THRESHOLD);
|
|
534
707
|
const rules = buildRuleRanking(allObjects.filter((o) => o.type === 'rule'), input.signals);
|
|
535
|
-
const
|
|
708
|
+
const weeklyActivity = buildWeeklyActivity(events, input.signals, now, weeks);
|
|
536
709
|
const outliers = buildOutliers(input.runLogText, input.pricing, input.topOutliers ?? 10);
|
|
537
|
-
const
|
|
710
|
+
const acceptance = buildAcceptance(input.signals);
|
|
711
|
+
const agents = buildAgents(input.signals, allObjects, input.pricing, acceptance.acceptedByAgent);
|
|
538
712
|
const steward = buildSteward(events, input.signals, allObjects, now, weeks);
|
|
539
713
|
const readiness = buildReadiness(input.signals);
|
|
714
|
+
const councils = await buildCouncils(allObjects, deps.relations, now, weeks);
|
|
715
|
+
const coverage = buildCoverage(input.signals);
|
|
716
|
+
// D7: без signalLogStats (старые вызывающие) → n/a; totalLines=0 → null-процент
|
|
717
|
+
const dataQuality = input.signalLogStats === undefined
|
|
718
|
+
? { validEventRatePct: null, malformedLines: 0 }
|
|
719
|
+
: {
|
|
720
|
+
validEventRatePct: input.signalLogStats.totalLines > 0
|
|
721
|
+
? ((input.signalLogStats.totalLines - input.signalLogStats.malformedLines) /
|
|
722
|
+
input.signalLogStats.totalLines) *
|
|
723
|
+
100
|
|
724
|
+
: null,
|
|
725
|
+
malformedLines: input.signalLogStats.malformedLines,
|
|
726
|
+
};
|
|
540
727
|
return {
|
|
541
728
|
generatedAt: now.toISOString(),
|
|
542
729
|
thresholds,
|
|
543
730
|
memory,
|
|
544
731
|
tools,
|
|
545
732
|
rules,
|
|
546
|
-
|
|
733
|
+
weeklyActivity,
|
|
547
734
|
outliers,
|
|
548
735
|
agents,
|
|
549
736
|
steward,
|
|
550
737
|
readiness,
|
|
738
|
+
councils,
|
|
739
|
+
acceptance: { accepted: acceptance.accepted, costPerAcceptedTask: acceptance.costPerAcceptedTask },
|
|
740
|
+
coverage,
|
|
741
|
+
dataQuality,
|
|
551
742
|
};
|
|
552
743
|
}
|
|
553
744
|
/** Срез отчёта по view-фильтру (§6.2); top ограничивает строки, дефолт 20. */
|
|
@@ -574,8 +765,8 @@ export function filterAnalytics(report, filter) {
|
|
|
574
765
|
rows = rows.filter((r) => r.silent === filter.silent);
|
|
575
766
|
return { view: 'rules', rows: rows.slice(0, top) };
|
|
576
767
|
}
|
|
577
|
-
case '
|
|
578
|
-
return { view: '
|
|
768
|
+
case 'weeklyActivity':
|
|
769
|
+
return { view: 'weeklyActivity', weeks: report.weeklyActivity };
|
|
579
770
|
case 'agents': {
|
|
580
771
|
let rows = report.agents;
|
|
581
772
|
if (filter.agent !== undefined)
|
|
@@ -588,6 +779,8 @@ export function filterAnalytics(report, filter) {
|
|
|
588
779
|
return { view: 'outliers', runs: report.outliers.slice(0, top) };
|
|
589
780
|
case 'readiness':
|
|
590
781
|
return { view: 'readiness', readiness: report.readiness };
|
|
782
|
+
case 'councils':
|
|
783
|
+
return { view: 'councils', councils: report.councils };
|
|
591
784
|
case 'all':
|
|
592
785
|
return { view: 'all', report };
|
|
593
786
|
}
|