crewly 1.20.395 → 1.20.396
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/backend/backend/src/constants.d.ts +27 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +27 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +23 -3
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-knowledge-gaps.service.d.ts +198 -0
- package/dist/backend/backend/src/services/wiki/wiki-knowledge-gaps.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-knowledge-gaps.service.js +366 -0
- package/dist/backend/backend/src/services/wiki/wiki-knowledge-gaps.service.js.map +1 -0
- package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.d.ts +6 -1
- package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.js +8 -2
- package/dist/backend/backend/src/services/wiki/wiki-maintenance.service.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +27 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +27 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WikiKnowledgeGapsService — turns the week's zero-hit queries into a page
|
|
3
|
+
* and one piece of work for the team lead (#914 P1).
|
|
4
|
+
*
|
|
5
|
+
* `wiki-query` and `recall` append every query to `llm-curated/usage.jsonl`,
|
|
6
|
+
* including the ones that found nothing. The bookkeep report listed five of
|
|
7
|
+
* them, and nobody read the bookkeep report. Once per ISO week, from the
|
|
8
|
+
* maintenance daily pass, this service:
|
|
9
|
+
*
|
|
10
|
+
* 1. reads the window's ledger events and keeps the misses (`miss` or an
|
|
11
|
+
* empty hit list);
|
|
12
|
+
* 2. folds them by normalised query text into distinct gaps — how often,
|
|
13
|
+
* who asked, first and last time — and drops a gap the vault answered
|
|
14
|
+
* later in the window (the same question later returned hits);
|
|
15
|
+
* 3. writes `llm-curated/gaps/<week>.md` through the normal ingest path
|
|
16
|
+
* (frontmatter, index line, history) — replaced, not appended, when the
|
|
17
|
+
* same week is compiled again;
|
|
18
|
+
* 4. hands the vault's team lead (the orchestrator for the global vault)
|
|
19
|
+
* one WorkItem to decide, per gap, whether it deserves a page. Never
|
|
20
|
+
* when the lead is dormant: a chore is not worth a wake.
|
|
21
|
+
*
|
|
22
|
+
* No LLM. A short query (`QUERY_MIN_CHARS`) is a stray token and ignored; a
|
|
23
|
+
* long one (`QUERY_MAX_CHARS`) is pasted text, counted but not listed.
|
|
24
|
+
*
|
|
25
|
+
* @module services/wiki/wiki-knowledge-gaps.service
|
|
26
|
+
*/
|
|
27
|
+
import { type WorkItem } from '../../types/v2/work-item.types.js';
|
|
28
|
+
import { WikiIngestService } from './wiki-ingest.service.js';
|
|
29
|
+
import { WikiUsageService, type WikiUsageEvent } from './wiki-usage.service.js';
|
|
30
|
+
/** One distinct unanswered question. */
|
|
31
|
+
export interface KnowledgeGap {
|
|
32
|
+
/** The query as first asked (original casing, whitespace collapsed). */
|
|
33
|
+
query: string;
|
|
34
|
+
/** Times it was asked and found nothing in the window. */
|
|
35
|
+
count: number;
|
|
36
|
+
/** Agents that asked, most frequent first (capped). */
|
|
37
|
+
agents: string[];
|
|
38
|
+
/** Retrieval paths it came through (`recall`, `wiki-query`, …). */
|
|
39
|
+
via: string[];
|
|
40
|
+
/** ISO time of the first and last miss. */
|
|
41
|
+
firstAt: string;
|
|
42
|
+
lastAt: string;
|
|
43
|
+
}
|
|
44
|
+
/** Outcome of folding a window's events. */
|
|
45
|
+
export interface KnowledgeGapRollup {
|
|
46
|
+
/** Every ledger event in the window. */
|
|
47
|
+
queries: number;
|
|
48
|
+
/** Events that found nothing. */
|
|
49
|
+
misses: number;
|
|
50
|
+
/** Distinct gaps, most-asked first (capped at `MAX_GAPS_PER_PAGE`). */
|
|
51
|
+
gaps: KnowledgeGap[];
|
|
52
|
+
/** Distinct gaps before the cap. */
|
|
53
|
+
distinct: number;
|
|
54
|
+
/** Misses whose query looked like pasted text (longer than `QUERY_MAX_CHARS`). */
|
|
55
|
+
pastedText: number;
|
|
56
|
+
/** Distinct questions that missed first and were answered later in the window. */
|
|
57
|
+
answeredLater: number;
|
|
58
|
+
}
|
|
59
|
+
/** The pool slice this service needs. */
|
|
60
|
+
export interface KnowledgeGapsPool {
|
|
61
|
+
addToPool(workItem: WorkItem): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
/** Construction options. */
|
|
64
|
+
export interface WikiKnowledgeGapsOptions {
|
|
65
|
+
ingest?: WikiIngestService;
|
|
66
|
+
usage?: WikiUsageService;
|
|
67
|
+
/** Where the lead's review WorkItem goes. Omit to write the page only. */
|
|
68
|
+
pool?: KnowledgeGapsPool;
|
|
69
|
+
/** Team lead for a vault; `null` falls back to `fallbackTarget`. */
|
|
70
|
+
resolveTarget?: (vaultPath: string) => Promise<string | null>;
|
|
71
|
+
/** Whether a target may be given work now; default: always. */
|
|
72
|
+
isTargetAwake?: (sessionName: string) => Promise<boolean>;
|
|
73
|
+
/** Target when no lead resolves (default: the orchestrator). */
|
|
74
|
+
fallbackTarget?: string;
|
|
75
|
+
}
|
|
76
|
+
/** Options for {@link WikiKnowledgeGapsService.compile}. */
|
|
77
|
+
export interface WikiKnowledgeGapsCompileOptions {
|
|
78
|
+
/** Report only; write nothing (page, state or WorkItem). */
|
|
79
|
+
dryRun?: boolean;
|
|
80
|
+
/** Clock (tests). */
|
|
81
|
+
now?: number;
|
|
82
|
+
/** Window override (tests). */
|
|
83
|
+
windowDays?: number;
|
|
84
|
+
/** Compile even when this week was already compiled (the page is replaced; no second WorkItem). */
|
|
85
|
+
force?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/** Outcome of one vault's compile. */
|
|
88
|
+
export interface WikiKnowledgeGapsResult {
|
|
89
|
+
vaultPath: string;
|
|
90
|
+
/** ISO week the page is for. */
|
|
91
|
+
week: string;
|
|
92
|
+
/** False when nothing was (or would be) written. */
|
|
93
|
+
compiled: boolean;
|
|
94
|
+
reason?: 'not_due' | 'no_queries' | 'no_gaps' | 'ingest_refused';
|
|
95
|
+
rollup: KnowledgeGapRollup;
|
|
96
|
+
/** Vault-relative page (planned in a dry run). */
|
|
97
|
+
pagePath?: string;
|
|
98
|
+
/** Lead WorkItem created this run. */
|
|
99
|
+
workItemId?: string;
|
|
100
|
+
/** Who the WorkItem went to (or would go to). */
|
|
101
|
+
target?: string;
|
|
102
|
+
/** Why no WorkItem was created although the page was. */
|
|
103
|
+
workItemSkipped?: 'no_pool' | 'target_dormant' | 'already_raised' | 'dry_run';
|
|
104
|
+
dryRun: boolean;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Normalise a query for grouping: case, whitespace, trailing punctuation.
|
|
108
|
+
*
|
|
109
|
+
* @param query - Raw query text
|
|
110
|
+
* @returns Grouping key
|
|
111
|
+
*/
|
|
112
|
+
export declare function gapKey(query: string): string;
|
|
113
|
+
/**
|
|
114
|
+
* Fold a window's ledger events into distinct gaps. Pure.
|
|
115
|
+
*
|
|
116
|
+
* @param events - Ledger events, any order
|
|
117
|
+
* @returns Counts and the distinct gaps, most-asked first then most recent
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```typescript
|
|
121
|
+
* const r = rollupGaps(events);
|
|
122
|
+
* // r.gaps[0] === { query: 'refund policy', count: 3, agents: ['kai'], … }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export declare function rollupGaps(events: readonly WikiUsageEvent[]): KnowledgeGapRollup;
|
|
126
|
+
/**
|
|
127
|
+
* Weekly knowledge-gap pages per vault. Singleton apart from the per-vault
|
|
128
|
+
* state file; tests construct their own.
|
|
129
|
+
*/
|
|
130
|
+
export declare class WikiKnowledgeGapsService {
|
|
131
|
+
private static instance;
|
|
132
|
+
private readonly logger;
|
|
133
|
+
private readonly ingest;
|
|
134
|
+
private readonly usage;
|
|
135
|
+
private readonly pool;
|
|
136
|
+
private readonly resolveTarget;
|
|
137
|
+
private readonly isTargetAwake;
|
|
138
|
+
private readonly fallbackTarget;
|
|
139
|
+
constructor(opts?: WikiKnowledgeGapsOptions);
|
|
140
|
+
static getInstance(): WikiKnowledgeGapsService;
|
|
141
|
+
/** Wire the production instance (built with a pool and a lead resolver). */
|
|
142
|
+
static setInstance(next: WikiKnowledgeGapsService | null): void;
|
|
143
|
+
/** Test-only reset. */
|
|
144
|
+
static _resetForTesting(): void;
|
|
145
|
+
/**
|
|
146
|
+
* Compile one vault's gap page for the current ISO week, once per week.
|
|
147
|
+
*
|
|
148
|
+
* @param vaultPath - Vault root
|
|
149
|
+
* @param opts - Dry run, clock, window and force
|
|
150
|
+
* @returns What was (or would be) written
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const r = await WikiKnowledgeGapsService.getInstance().compile(vault);
|
|
155
|
+
* // r.compiled && r.pagePath === 'llm-curated/gaps/2026-W43.md'
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
compile(vaultPath: string, opts?: WikiKnowledgeGapsCompileOptions): Promise<WikiKnowledgeGapsResult>;
|
|
159
|
+
/**
|
|
160
|
+
* Compile every vault in a list; failures are logged per vault.
|
|
161
|
+
*
|
|
162
|
+
* @param vaults - Vault roots
|
|
163
|
+
* @param opts - Passed to {@link compile}
|
|
164
|
+
* @returns One result per vault that did not throw
|
|
165
|
+
*/
|
|
166
|
+
compileAll(vaults: readonly string[], opts?: WikiKnowledgeGapsCompileOptions): Promise<WikiKnowledgeGapsResult[]>;
|
|
167
|
+
/**
|
|
168
|
+
* Markdown body: a dated heading with the counts, one bullet per gap
|
|
169
|
+
* (query, times asked, who, last asked, path), then what the lead does
|
|
170
|
+
* with it.
|
|
171
|
+
*
|
|
172
|
+
* @param rollup - Folded gaps
|
|
173
|
+
* @param now - Epoch ms of the run
|
|
174
|
+
* @param windowDays - Window the counts cover
|
|
175
|
+
* @returns Markdown
|
|
176
|
+
*/
|
|
177
|
+
renderBody(rollup: KnowledgeGapRollup, now: number, windowDays: number): string;
|
|
178
|
+
/**
|
|
179
|
+
* The lead's review WorkItem: title with the vault and the count, the
|
|
180
|
+
* rendered page as the brief.
|
|
181
|
+
*/
|
|
182
|
+
private buildWorkItem;
|
|
183
|
+
/** Who reviews a vault's gaps: its team lead, else the fallback. */
|
|
184
|
+
private targetFor;
|
|
185
|
+
/** Errors count as dormant: a chore is never worth risking a wake. */
|
|
186
|
+
private targetAwake;
|
|
187
|
+
private statePath;
|
|
188
|
+
private readState;
|
|
189
|
+
private writeState;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Short human label for a vault: the project or team folder it belongs to.
|
|
193
|
+
*
|
|
194
|
+
* @param vaultPath - `<project>/.crewly/wiki`, `~/.crewly/teams/<id>/wiki` or `~/.crewly/global-wiki`
|
|
195
|
+
* @returns `crewly`, `<team-id>` or `global`
|
|
196
|
+
*/
|
|
197
|
+
export declare function vaultLabel(vaultPath: string): string;
|
|
198
|
+
//# sourceMappingURL=wiki-knowledge-gaps.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-knowledge-gaps.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-knowledge-gaps.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WikiKnowledgeGapsService — turns the week's zero-hit queries into a page
|
|
3
|
+
* and one piece of work for the team lead (#914 P1).
|
|
4
|
+
*
|
|
5
|
+
* `wiki-query` and `recall` append every query to `llm-curated/usage.jsonl`,
|
|
6
|
+
* including the ones that found nothing. The bookkeep report listed five of
|
|
7
|
+
* them, and nobody read the bookkeep report. Once per ISO week, from the
|
|
8
|
+
* maintenance daily pass, this service:
|
|
9
|
+
*
|
|
10
|
+
* 1. reads the window's ledger events and keeps the misses (`miss` or an
|
|
11
|
+
* empty hit list);
|
|
12
|
+
* 2. folds them by normalised query text into distinct gaps — how often,
|
|
13
|
+
* who asked, first and last time — and drops a gap the vault answered
|
|
14
|
+
* later in the window (the same question later returned hits);
|
|
15
|
+
* 3. writes `llm-curated/gaps/<week>.md` through the normal ingest path
|
|
16
|
+
* (frontmatter, index line, history) — replaced, not appended, when the
|
|
17
|
+
* same week is compiled again;
|
|
18
|
+
* 4. hands the vault's team lead (the orchestrator for the global vault)
|
|
19
|
+
* one WorkItem to decide, per gap, whether it deserves a page. Never
|
|
20
|
+
* when the lead is dormant: a chore is not worth a wake.
|
|
21
|
+
*
|
|
22
|
+
* No LLM. A short query (`QUERY_MIN_CHARS`) is a stray token and ignored; a
|
|
23
|
+
* long one (`QUERY_MAX_CHARS`) is pasted text, counted but not listed.
|
|
24
|
+
*
|
|
25
|
+
* @module services/wiki/wiki-knowledge-gaps.service
|
|
26
|
+
*/
|
|
27
|
+
import * as path from 'path';
|
|
28
|
+
import { LoggerService } from '../core/logger.service.js';
|
|
29
|
+
import { ORCHESTRATOR_SESSION_NAME, WIKI_GAPS_CONSTANTS } from '../../constants.js';
|
|
30
|
+
import { atomicWriteJson, safeReadJson } from '../../utils/file-io.utils.js';
|
|
31
|
+
import { createWorkItem } from '../../types/v2/work-item.types.js';
|
|
32
|
+
import { WikiIngestService } from './wiki-ingest.service.js';
|
|
33
|
+
import { WikiUsageService } from './wiki-usage.service.js';
|
|
34
|
+
import { isoWeekLabel } from './wiki-log-compile.service.js';
|
|
35
|
+
/**
|
|
36
|
+
* Normalise a query for grouping: case, whitespace, trailing punctuation.
|
|
37
|
+
*
|
|
38
|
+
* @param query - Raw query text
|
|
39
|
+
* @returns Grouping key
|
|
40
|
+
*/
|
|
41
|
+
export function gapKey(query) {
|
|
42
|
+
return query
|
|
43
|
+
.toLowerCase()
|
|
44
|
+
.replace(/\s+/g, ' ')
|
|
45
|
+
.replace(/[\s.,;:!?。,;:!?]+$/u, '')
|
|
46
|
+
.trim();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Fold a window's ledger events into distinct gaps. Pure.
|
|
50
|
+
*
|
|
51
|
+
* @param events - Ledger events, any order
|
|
52
|
+
* @returns Counts and the distinct gaps, most-asked first then most recent
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const r = rollupGaps(events);
|
|
57
|
+
* // r.gaps[0] === { query: 'refund policy', count: 3, agents: ['kai'], … }
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function rollupGaps(events) {
|
|
61
|
+
const sorted = [...events].sort((a, b) => Date.parse(a.at) - Date.parse(b.at));
|
|
62
|
+
const answered = new Map(); // key → last time it returned hits (ms)
|
|
63
|
+
for (const e of sorted) {
|
|
64
|
+
if (!e.miss && Array.isArray(e.hits) && e.hits.length > 0)
|
|
65
|
+
answered.set(gapKey(e.query ?? ''), Date.parse(e.at));
|
|
66
|
+
}
|
|
67
|
+
const byKey = new Map();
|
|
68
|
+
let misses = 0;
|
|
69
|
+
let pastedText = 0;
|
|
70
|
+
for (const e of sorted) {
|
|
71
|
+
const isMiss = e.miss === true || !Array.isArray(e.hits) || e.hits.length === 0;
|
|
72
|
+
if (!isMiss)
|
|
73
|
+
continue;
|
|
74
|
+
misses++;
|
|
75
|
+
const text = (e.query ?? '').replace(/\s+/g, ' ').trim();
|
|
76
|
+
const key = gapKey(text);
|
|
77
|
+
if (key.length < WIKI_GAPS_CONSTANTS.QUERY_MIN_CHARS)
|
|
78
|
+
continue;
|
|
79
|
+
if (text.length > WIKI_GAPS_CONSTANTS.QUERY_MAX_CHARS) {
|
|
80
|
+
pastedText++;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
const agent = e.agent || 'unknown';
|
|
84
|
+
const slot = byKey.get(key);
|
|
85
|
+
if (slot) {
|
|
86
|
+
slot.gap.count++;
|
|
87
|
+
slot.gap.lastAt = e.at;
|
|
88
|
+
slot.agents.set(agent, (slot.agents.get(agent) ?? 0) + 1);
|
|
89
|
+
slot.via.add(e.via);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
byKey.set(key, {
|
|
93
|
+
gap: { query: text, count: 1, agents: [], via: [], firstAt: e.at, lastAt: e.at },
|
|
94
|
+
agents: new Map([[agent, 1]]),
|
|
95
|
+
via: new Set([e.via]),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let answeredLater = 0;
|
|
100
|
+
const gaps = [];
|
|
101
|
+
for (const [key, slot] of byKey) {
|
|
102
|
+
const hitAt = answered.get(key);
|
|
103
|
+
if (hitAt !== undefined && hitAt > Date.parse(slot.gap.lastAt)) {
|
|
104
|
+
answeredLater++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
slot.gap.agents = [...slot.agents.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([a]) => a).slice(0, WIKI_GAPS_CONSTANTS.MAX_AGENTS_PER_GAP);
|
|
108
|
+
slot.gap.via = [...slot.via].sort();
|
|
109
|
+
gaps.push(slot.gap);
|
|
110
|
+
}
|
|
111
|
+
gaps.sort((a, b) => b.count - a.count || Date.parse(b.lastAt) - Date.parse(a.lastAt) || a.query.localeCompare(b.query));
|
|
112
|
+
return {
|
|
113
|
+
queries: events.length,
|
|
114
|
+
misses,
|
|
115
|
+
gaps: gaps.slice(0, WIKI_GAPS_CONSTANTS.MAX_GAPS_PER_PAGE),
|
|
116
|
+
distinct: gaps.length,
|
|
117
|
+
pastedText,
|
|
118
|
+
answeredLater,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Weekly knowledge-gap pages per vault. Singleton apart from the per-vault
|
|
123
|
+
* state file; tests construct their own.
|
|
124
|
+
*/
|
|
125
|
+
export class WikiKnowledgeGapsService {
|
|
126
|
+
static instance = null;
|
|
127
|
+
logger;
|
|
128
|
+
ingest;
|
|
129
|
+
usage;
|
|
130
|
+
pool;
|
|
131
|
+
resolveTarget;
|
|
132
|
+
isTargetAwake;
|
|
133
|
+
fallbackTarget;
|
|
134
|
+
constructor(opts = {}) {
|
|
135
|
+
this.logger = LoggerService.getInstance().createComponentLogger('WikiKnowledgeGaps');
|
|
136
|
+
this.ingest = opts.ingest ?? WikiIngestService.getInstance();
|
|
137
|
+
this.usage = opts.usage ?? WikiUsageService.getInstance();
|
|
138
|
+
this.pool = opts.pool ?? null;
|
|
139
|
+
this.resolveTarget = opts.resolveTarget ?? null;
|
|
140
|
+
this.isTargetAwake = opts.isTargetAwake ?? null;
|
|
141
|
+
this.fallbackTarget = opts.fallbackTarget ?? ORCHESTRATOR_SESSION_NAME;
|
|
142
|
+
}
|
|
143
|
+
static getInstance() {
|
|
144
|
+
if (!this.instance)
|
|
145
|
+
this.instance = new WikiKnowledgeGapsService();
|
|
146
|
+
return this.instance;
|
|
147
|
+
}
|
|
148
|
+
/** Wire the production instance (built with a pool and a lead resolver). */
|
|
149
|
+
static setInstance(next) {
|
|
150
|
+
this.instance = next;
|
|
151
|
+
}
|
|
152
|
+
/** Test-only reset. */
|
|
153
|
+
static _resetForTesting() {
|
|
154
|
+
this.instance = null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Compile one vault's gap page for the current ISO week, once per week.
|
|
158
|
+
*
|
|
159
|
+
* @param vaultPath - Vault root
|
|
160
|
+
* @param opts - Dry run, clock, window and force
|
|
161
|
+
* @returns What was (or would be) written
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const r = await WikiKnowledgeGapsService.getInstance().compile(vault);
|
|
166
|
+
* // r.compiled && r.pagePath === 'llm-curated/gaps/2026-W43.md'
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
async compile(vaultPath, opts = {}) {
|
|
170
|
+
const dryRun = opts.dryRun === true;
|
|
171
|
+
const now = opts.now ?? Date.now();
|
|
172
|
+
const windowDays = opts.windowDays ?? WIKI_GAPS_CONSTANTS.WINDOW_DAYS;
|
|
173
|
+
const week = isoWeekLabel(now);
|
|
174
|
+
const empty = { queries: 0, misses: 0, gaps: [], distinct: 0, pastedText: 0, answeredLater: 0 };
|
|
175
|
+
const base = { vaultPath, week, compiled: false, rollup: empty, dryRun };
|
|
176
|
+
const state = await this.readState(vaultPath);
|
|
177
|
+
if (!opts.force && state.lastWeek === week)
|
|
178
|
+
return { ...base, reason: 'not_due' };
|
|
179
|
+
const usage = new WikiUsageService(() => new Date(now));
|
|
180
|
+
const events = await usage.events(vaultPath, windowDays);
|
|
181
|
+
if (events.length === 0)
|
|
182
|
+
return { ...base, reason: 'no_queries' };
|
|
183
|
+
const rollup = rollupGaps(events);
|
|
184
|
+
if (rollup.gaps.length === 0)
|
|
185
|
+
return { ...base, rollup, reason: 'no_gaps' };
|
|
186
|
+
const pagePath = `${WIKI_GAPS_CONSTANTS.DIR}/${week}.md`;
|
|
187
|
+
const body = this.renderBody(rollup, now, windowDays);
|
|
188
|
+
const result = { ...base, compiled: true, rollup, pagePath };
|
|
189
|
+
const target = await this.targetFor(vaultPath);
|
|
190
|
+
result.target = target;
|
|
191
|
+
if (dryRun) {
|
|
192
|
+
result.workItemSkipped = 'dry_run';
|
|
193
|
+
this.logger.info('WikiKnowledgeGaps dry run — would write gap page', { vaultPath, pagePath, gaps: rollup.gaps.length, target });
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
const n = rollup.gaps.length;
|
|
197
|
+
const outcome = await this.ingest.ingest({
|
|
198
|
+
vaultPath,
|
|
199
|
+
sourceType: 'record_learning',
|
|
200
|
+
sourceRef: `wiki-knowledge-gaps:${week}:${new Date(now).toISOString()}`,
|
|
201
|
+
sourceBody: body,
|
|
202
|
+
callerSession: WIKI_GAPS_CONSTANTS.CALLER_SESSION,
|
|
203
|
+
targetRelativePath: pagePath,
|
|
204
|
+
title: `Knowledge gaps ${week}`,
|
|
205
|
+
summary: `${n} question${n === 1 ? '' : 's'} agents asked in the last ${windowDays} days that this vault could not answer — the team lead decides which deserve a page.`,
|
|
206
|
+
keepBecause: 'changes_decision',
|
|
207
|
+
tags: ['knowledge-gaps', week],
|
|
208
|
+
replace: true,
|
|
209
|
+
});
|
|
210
|
+
if (!outcome.ok) {
|
|
211
|
+
this.logger.warn('WikiKnowledgeGaps: ingest refused the gap page (non-fatal)', { vaultPath, pagePath, reason: outcome.reason });
|
|
212
|
+
return { ...base, rollup, reason: 'ingest_refused' };
|
|
213
|
+
}
|
|
214
|
+
let workItemId = state.lastWeek === week ? state.workItemId : null;
|
|
215
|
+
if (workItemId) {
|
|
216
|
+
result.workItemSkipped = 'already_raised';
|
|
217
|
+
}
|
|
218
|
+
else if (!this.pool) {
|
|
219
|
+
result.workItemSkipped = 'no_pool';
|
|
220
|
+
}
|
|
221
|
+
else if (!(await this.targetAwake(target))) {
|
|
222
|
+
result.workItemSkipped = 'target_dormant';
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
const wi = this.buildWorkItem(vaultPath, week, target, pagePath, rollup, body);
|
|
226
|
+
await this.pool.addToPool(wi);
|
|
227
|
+
workItemId = wi.id;
|
|
228
|
+
result.workItemId = wi.id;
|
|
229
|
+
}
|
|
230
|
+
await this.writeState(vaultPath, { lastWeek: week, lastCompiledAt: new Date(now).toISOString(), workItemId });
|
|
231
|
+
this.logger.info('WikiKnowledgeGaps wrote gap page', { vaultPath, pagePath, gaps: n, misses: rollup.misses, target, workItemId: result.workItemId ?? null, skipped: result.workItemSkipped ?? null });
|
|
232
|
+
return result;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Compile every vault in a list; failures are logged per vault.
|
|
236
|
+
*
|
|
237
|
+
* @param vaults - Vault roots
|
|
238
|
+
* @param opts - Passed to {@link compile}
|
|
239
|
+
* @returns One result per vault that did not throw
|
|
240
|
+
*/
|
|
241
|
+
async compileAll(vaults, opts = {}) {
|
|
242
|
+
const out = [];
|
|
243
|
+
for (const vaultPath of vaults) {
|
|
244
|
+
try {
|
|
245
|
+
out.push(await this.compile(vaultPath, opts));
|
|
246
|
+
}
|
|
247
|
+
catch (err) {
|
|
248
|
+
this.logger.warn('WikiKnowledgeGaps failed for vault (non-fatal)', { vaultPath, error: err instanceof Error ? err.message : String(err) });
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
/**
|
|
255
|
+
* Markdown body: a dated heading with the counts, one bullet per gap
|
|
256
|
+
* (query, times asked, who, last asked, path), then what the lead does
|
|
257
|
+
* with it.
|
|
258
|
+
*
|
|
259
|
+
* @param rollup - Folded gaps
|
|
260
|
+
* @param now - Epoch ms of the run
|
|
261
|
+
* @param windowDays - Window the counts cover
|
|
262
|
+
* @returns Markdown
|
|
263
|
+
*/
|
|
264
|
+
renderBody(rollup, now, windowDays) {
|
|
265
|
+
const date = new Date(now).toISOString().slice(0, 10);
|
|
266
|
+
const lines = [
|
|
267
|
+
`## Compiled ${date} — ${rollup.queries} queries in ${windowDays} days, ${rollup.misses} found nothing, ${rollup.distinct} distinct gap${rollup.distinct === 1 ? '' : 's'}`,
|
|
268
|
+
'',
|
|
269
|
+
];
|
|
270
|
+
const notes = [];
|
|
271
|
+
if (rollup.answeredLater > 0)
|
|
272
|
+
notes.push(`${rollup.answeredLater} question${rollup.answeredLater === 1 ? '' : 's'} missed first and found a page later in the week — not listed.`);
|
|
273
|
+
if (rollup.pastedText > 0)
|
|
274
|
+
notes.push(`${rollup.pastedText} miss${rollup.pastedText === 1 ? '' : 'es'} looked like pasted text (longer than ${WIKI_GAPS_CONSTANTS.QUERY_MAX_CHARS} characters) — counted, not listed.`);
|
|
275
|
+
if (rollup.distinct > rollup.gaps.length)
|
|
276
|
+
notes.push(`Only the ${rollup.gaps.length} most-asked of ${rollup.distinct} gaps are listed.`);
|
|
277
|
+
if (notes.length)
|
|
278
|
+
lines.push(...notes.map((t) => `_${t}_`), '');
|
|
279
|
+
lines.push(`### Unanswered questions (${rollup.gaps.length})`, '');
|
|
280
|
+
for (const g of rollup.gaps) {
|
|
281
|
+
const who = g.agents.join(', ');
|
|
282
|
+
const times = g.count === 1 ? 'asked once' : `asked ${g.count}×`;
|
|
283
|
+
lines.push(`- **${g.query}** — ${times} by ${who}; last ${g.lastAt.slice(0, 10)} via ${g.via.join('/')}`);
|
|
284
|
+
}
|
|
285
|
+
lines.push('', '### What to do', '', '- For each question that has an answer worth keeping: write the page (wiki-ingest with a one-line summary and a keep_because) so the next search finds it.', '- For a question that does not deserve a page (one-off, already covered under other words, or not this vault\'s business): say so in the WorkItem result, one line per question.', '- The owner is not involved: never post this list, or the fact that you worked it, in the owner\'s thread.');
|
|
286
|
+
return lines.join('\n').trim();
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The lead's review WorkItem: title with the vault and the count, the
|
|
290
|
+
* rendered page as the brief.
|
|
291
|
+
*/
|
|
292
|
+
buildWorkItem(vaultPath, week, target, pagePath, rollup, body) {
|
|
293
|
+
const n = rollup.gaps.length;
|
|
294
|
+
const label = vaultLabel(vaultPath);
|
|
295
|
+
return createWorkItem({
|
|
296
|
+
type: 'delegate',
|
|
297
|
+
owner: target === ORCHESTRATOR_SESSION_NAME ? 'orchestrator' : 'team_lead',
|
|
298
|
+
target,
|
|
299
|
+
title: `Review knowledge gaps ${week} — ${label} (${n} unanswered question${n === 1 ? '' : 's'})`,
|
|
300
|
+
description: `Agents asked ${n} question${n === 1 ? '' : 's'} this week that the ${label} vault could not answer. Read ${pagePath} (vault ${vaultPath}) and decide, per question, whether it deserves a page: write it with wiki-ingest, or say in one line why not. Do not involve the owner.`,
|
|
301
|
+
briefMarkdown: `# Knowledge gaps ${week} — ${label}\n\nVault: \`${vaultPath}\`\nPage: \`${pagePath}\`\n\n${body}`,
|
|
302
|
+
maxRetries: 1,
|
|
303
|
+
metadata: {
|
|
304
|
+
kind: WIKI_GAPS_CONSTANTS.WORKITEM_KIND,
|
|
305
|
+
vaultPath,
|
|
306
|
+
week,
|
|
307
|
+
pagePath,
|
|
308
|
+
gaps: n,
|
|
309
|
+
autoCreated: true,
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/** Who reviews a vault's gaps: its team lead, else the fallback. */
|
|
314
|
+
async targetFor(vaultPath) {
|
|
315
|
+
if (!this.resolveTarget)
|
|
316
|
+
return this.fallbackTarget;
|
|
317
|
+
try {
|
|
318
|
+
return (await this.resolveTarget(vaultPath)) ?? this.fallbackTarget;
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
return this.fallbackTarget;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/** Errors count as dormant: a chore is never worth risking a wake. */
|
|
325
|
+
async targetAwake(target) {
|
|
326
|
+
if (!this.isTargetAwake)
|
|
327
|
+
return true;
|
|
328
|
+
try {
|
|
329
|
+
return await this.isTargetAwake(target);
|
|
330
|
+
}
|
|
331
|
+
catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
statePath(vaultPath) {
|
|
336
|
+
return path.join(vaultPath, WIKI_GAPS_CONSTANTS.STATE_FILE);
|
|
337
|
+
}
|
|
338
|
+
async readState(vaultPath) {
|
|
339
|
+
const data = await safeReadJson(this.statePath(vaultPath), null);
|
|
340
|
+
return {
|
|
341
|
+
lastWeek: typeof data?.lastWeek === 'string' ? data.lastWeek : null,
|
|
342
|
+
lastCompiledAt: typeof data?.lastCompiledAt === 'string' ? data.lastCompiledAt : null,
|
|
343
|
+
workItemId: typeof data?.workItemId === 'string' ? data.workItemId : null,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
async writeState(vaultPath, state) {
|
|
347
|
+
await atomicWriteJson(this.statePath(vaultPath), state);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Short human label for a vault: the project or team folder it belongs to.
|
|
352
|
+
*
|
|
353
|
+
* @param vaultPath - `<project>/.crewly/wiki`, `~/.crewly/teams/<id>/wiki` or `~/.crewly/global-wiki`
|
|
354
|
+
* @returns `crewly`, `<team-id>` or `global`
|
|
355
|
+
*/
|
|
356
|
+
export function vaultLabel(vaultPath) {
|
|
357
|
+
const normalized = vaultPath.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
358
|
+
if (normalized.endsWith('/global-wiki'))
|
|
359
|
+
return 'global';
|
|
360
|
+
if (normalized.endsWith('/.crewly/wiki'))
|
|
361
|
+
return path.basename(normalized.slice(0, -'/.crewly/wiki'.length)) || 'project';
|
|
362
|
+
if (normalized.endsWith('/wiki'))
|
|
363
|
+
return path.basename(normalized.slice(0, -'/wiki'.length)) || 'vault';
|
|
364
|
+
return path.basename(normalized) || 'vault';
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=wiki-knowledge-gaps.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-knowledge-gaps.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-knowledge-gaps.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* One hourly tick:
|
|
5
5
|
* - every tick: a queue drain pass ({@link WikiQueueDrainService});
|
|
6
6
|
* - once per day (and on the first tick after boot when the last daily
|
|
7
|
-
* pass is older than a day): log rotation
|
|
7
|
+
* pass is older than a day): log rotation, digest compile and the
|
|
8
|
+
* knowledge-gap roll-up (#914, itself once per ISO week) for every
|
|
8
9
|
* discovered vault.
|
|
9
10
|
*
|
|
10
11
|
* The first tick runs `MAINTENANCE_BOOT_DELAY_MS` after `start()` so boot
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
import { WikiLogRotationService, type WikiLogRotateResult } from './wiki-log-rotation.service.js';
|
|
18
19
|
import { WikiLogCompileService, type WikiLogCompileResult } from './wiki-log-compile.service.js';
|
|
19
20
|
import { WikiQueueDrainService, type WikiQueueDrainResult } from './wiki-queue-drain.service.js';
|
|
21
|
+
import { WikiKnowledgeGapsService, type WikiKnowledgeGapsResult } from './wiki-knowledge-gaps.service.js';
|
|
20
22
|
/** Options for {@link WikiMaintenanceService}. */
|
|
21
23
|
export interface WikiMaintenanceOptions {
|
|
22
24
|
/** Tick interval (default hourly). */
|
|
@@ -33,6 +35,7 @@ export interface WikiMaintenanceOptions {
|
|
|
33
35
|
rotation?: WikiLogRotationService;
|
|
34
36
|
compile?: WikiLogCompileService;
|
|
35
37
|
drain?: WikiQueueDrainService;
|
|
38
|
+
gaps?: WikiKnowledgeGapsService;
|
|
36
39
|
/** Clock (tests). */
|
|
37
40
|
now?: () => number;
|
|
38
41
|
/** Dry run: every step reports and writes nothing. */
|
|
@@ -57,6 +60,7 @@ export interface WikiMaintenanceTickResult {
|
|
|
57
60
|
vaults: string[];
|
|
58
61
|
rotated: WikiLogRotateResult[];
|
|
59
62
|
compiled: WikiLogCompileResult[];
|
|
63
|
+
gaps: WikiKnowledgeGapsResult[];
|
|
60
64
|
};
|
|
61
65
|
skippedDaily?: 'not_due';
|
|
62
66
|
}
|
|
@@ -74,6 +78,7 @@ export declare class WikiMaintenanceService {
|
|
|
74
78
|
private readonly rotation;
|
|
75
79
|
private readonly compile;
|
|
76
80
|
private readonly drain;
|
|
81
|
+
private readonly gaps;
|
|
77
82
|
private readonly nowFn;
|
|
78
83
|
private readonly dryRun;
|
|
79
84
|
private readonly bootGate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wiki-maintenance.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-maintenance.service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"wiki-maintenance.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/wiki/wiki-maintenance.service.ts"],"names":[],"mappings":"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"}
|