mustflow 2.23.0 → 2.24.2
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/README.md +12 -2
- package/dist/cli/commands/adapters.js +11 -9
- package/dist/cli/commands/api.js +263 -113
- package/dist/cli/commands/check.js +11 -7
- package/dist/cli/commands/classify.js +16 -42
- package/dist/cli/commands/context.js +18 -31
- package/dist/cli/commands/contract-lint.js +12 -7
- package/dist/cli/commands/dashboard.js +65 -114
- package/dist/cli/commands/docs.js +43 -26
- package/dist/cli/commands/doctor.js +11 -7
- package/dist/cli/commands/evidence.js +642 -0
- package/dist/cli/commands/explain-verify.js +1 -59
- package/dist/cli/commands/explain.js +84 -36
- package/dist/cli/commands/handoff.js +13 -17
- package/dist/cli/commands/impact.js +14 -20
- package/dist/cli/commands/index.js +15 -9
- package/dist/cli/commands/init.js +56 -70
- package/dist/cli/commands/line-endings.js +15 -9
- package/dist/cli/commands/map.js +30 -42
- package/dist/cli/commands/next.js +300 -0
- package/dist/cli/commands/onboard.js +136 -0
- package/dist/cli/commands/run.js +47 -42
- package/dist/cli/commands/search.js +43 -69
- package/dist/cli/commands/status.js +9 -6
- package/dist/cli/commands/update.js +16 -10
- package/dist/cli/commands/upgrade.js +9 -6
- package/dist/cli/commands/verify/args.js +55 -249
- package/dist/cli/commands/verify.js +2 -1
- package/dist/cli/commands/version-sources.js +9 -6
- package/dist/cli/commands/version.js +9 -6
- package/dist/cli/commands/workspace.js +564 -0
- package/dist/cli/i18n/en.js +60 -1
- package/dist/cli/i18n/es.js +60 -1
- package/dist/cli/i18n/fr.js +60 -1
- package/dist/cli/i18n/hi.js +60 -1
- package/dist/cli/i18n/ko.js +60 -1
- package/dist/cli/i18n/zh.js +60 -1
- package/dist/cli/index.js +28 -25
- package/dist/cli/lib/agent-context.js +8 -9
- package/dist/cli/lib/command-registry.js +24 -0
- package/dist/cli/lib/dashboard-html/client-script.js +1 -1
- package/dist/cli/lib/local-index/database-path.js +5 -0
- package/dist/cli/lib/local-index/database-read.js +88 -0
- package/dist/cli/lib/local-index/effect-graph-read-model.js +112 -0
- package/dist/cli/lib/local-index/freshness.js +60 -0
- package/dist/cli/lib/local-index/index.js +12 -1866
- package/dist/cli/lib/local-index/path-surface-read-model.js +134 -0
- package/dist/cli/lib/local-index/populate.js +474 -0
- package/dist/cli/lib/local-index/schema.js +413 -0
- package/dist/cli/lib/local-index/search-read-model.js +533 -0
- package/dist/cli/lib/local-index/search-text.js +79 -0
- package/dist/cli/lib/option-parser.js +93 -0
- package/dist/cli/lib/repo-map.js +2 -2
- package/dist/cli/lib/run-plan.js +5 -22
- package/dist/core/change-verification.js +11 -5
- package/dist/core/command-effects.js +1 -3
- package/dist/core/command-intent-eligibility.js +14 -0
- package/dist/core/command-preconditions.js +8 -4
- package/dist/core/command-run-constraints.js +43 -0
- package/dist/core/public-json-contracts.js +57 -0
- package/dist/core/test-selection.js +8 -2
- package/dist/core/verification-plan.js +32 -4
- package/package.json +1 -1
- package/schemas/README.md +16 -0
- package/schemas/api-serve-response.schema.json +89 -0
- package/schemas/change-verification-report.schema.json +4 -1
- package/schemas/contract-lint-report.schema.json +1 -0
- package/schemas/evidence-report.schema.json +287 -0
- package/schemas/explain-report.schema.json +4 -0
- package/schemas/next-report.schema.json +121 -0
- package/schemas/onboard-commands-report.schema.json +100 -0
- package/schemas/workspace-command-catalog.schema.json +172 -0
- package/schemas/workspace-status.schema.json +141 -0
- package/schemas/workspace-verification-plan.schema.json +195 -0
- package/templates/default/manifest.toml +1 -1
|
@@ -1,145 +1,23 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { isRecord, readStringArray } from '../command-contract.js';
|
|
4
3
|
import { toPosixPath } from '../filesystem.js';
|
|
5
4
|
import { collectSourceAnchorIndexRecords, hasHighRiskSourceAnchorRiskTags, } from '../../../core/source-anchor-status.js';
|
|
6
|
-
import { listChangeClassificationRuleDescriptors } from '../../../core/change-classification.js';
|
|
7
5
|
import { writeFileInsideWithoutSymlinks } from '../../../core/safe-filesystem.js';
|
|
8
|
-
import {
|
|
6
|
+
import { LOCAL_INDEX_CONTENT_MODE, LOCAL_INDEX_EXCLUDED_RAW_DATA_KINDS, LOCAL_INDEX_PARSER_VERSION, LOCAL_INDEX_SCHEMA_VERSION, LOCAL_INDEX_STORE_FULL_CONTENT, MAX_SNIPPET_BYTES_PER_DOCUMENT, } from './constants.js';
|
|
9
7
|
import { collectCommandIntents } from './command-effect-index.js';
|
|
10
|
-
import {
|
|
8
|
+
import { getLocalIndexDatabasePath } from './database-path.js';
|
|
9
|
+
import { detectLocalSearchCapabilities, hasTable, queryRows, readCount, readMetadataValue, readStoredIndexedPaths, readStoredSearchCapabilities, searchCapabilities, splitIndexedList, toNullableNumber, toSearchString, } from './database-read.js';
|
|
10
|
+
import { getStalePaths, readStoredSchemaVersion } from './freshness.js';
|
|
11
|
+
import { createSourceAnchorRiskSignals, populateDatabase } from './populate.js';
|
|
12
|
+
import { createSchema } from './schema.js';
|
|
11
13
|
import { loadSqlJs } from './sql.js';
|
|
12
|
-
import { collectFastPreflightIndexedFileMetadataRecords, collectIndexedFileRecords, collectSourceAnchorCandidatePaths, getSourceScopeHash, hashIndexedFileMetadataRecords, normalizeIndexedFileSourceScope,
|
|
14
|
+
import { collectFastPreflightIndexedFileMetadataRecords, collectIndexedFileRecords, collectSourceAnchorCandidatePaths, getSourceScopeHash, hashIndexedFileMetadataRecords, normalizeIndexedFileSourceScope, readLocalIndexSourceConfig, } from './source-index.js';
|
|
13
15
|
import { createVerificationEvidenceIndex } from './verification-evidence.js';
|
|
14
|
-
import { collectDocuments,
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (!table || !isRecord(table[key])) {
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
return table[key];
|
|
23
|
-
}
|
|
24
|
-
function readOptionalStringArray(table, key) {
|
|
25
|
-
return table ? readStringArray(table, key) ?? null : null;
|
|
26
|
-
}
|
|
27
|
-
function normalizeSearchText(value) {
|
|
28
|
-
return value.trim().replace(/\s+/g, ' ');
|
|
29
|
-
}
|
|
30
|
-
function normalizeSearchTokenText(value) {
|
|
31
|
-
return normalizeSearchText(value).normalize('NFKC').toLowerCase();
|
|
32
|
-
}
|
|
33
|
-
function extractSearchTokens(value) {
|
|
34
|
-
return [...normalizeSearchTokenText(value).matchAll(/[\p{L}\p{N}]+/gu)]
|
|
35
|
-
.map((match) => match[0])
|
|
36
|
-
.filter((token) => Boolean(token));
|
|
37
|
-
}
|
|
38
|
-
function buildSearchNgrams(values) {
|
|
39
|
-
const grams = new Set();
|
|
40
|
-
for (const value of values) {
|
|
41
|
-
for (const token of extractSearchTokens(value)) {
|
|
42
|
-
const boundedToken = token.slice(0, SEARCH_NGRAM_MAX_TOKEN_CHARS);
|
|
43
|
-
const maxLength = Math.min(SEARCH_NGRAM_MAX_LENGTH, boundedToken.length);
|
|
44
|
-
for (let length = SEARCH_NGRAM_MIN_LENGTH; length <= maxLength; length += 1) {
|
|
45
|
-
for (let index = 0; index <= boundedToken.length - length; index += 1) {
|
|
46
|
-
grams.add(boundedToken.slice(index, index + length));
|
|
47
|
-
if (grams.size >= SEARCH_NGRAM_MAX_GRAMS_PER_TARGET) {
|
|
48
|
-
return [...grams].sort((left, right) => left.localeCompare(right));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return [...grams].sort((left, right) => left.localeCompare(right));
|
|
55
|
-
}
|
|
56
|
-
function toSearchString(value) {
|
|
57
|
-
if (value === null || value === undefined) {
|
|
58
|
-
return '';
|
|
59
|
-
}
|
|
60
|
-
if (value instanceof Uint8Array) {
|
|
61
|
-
return '';
|
|
62
|
-
}
|
|
63
|
-
return String(value);
|
|
64
|
-
}
|
|
65
|
-
function queryRows(database, sql, params = []) {
|
|
66
|
-
const [result] = database.exec(sql, params);
|
|
67
|
-
if (!result) {
|
|
68
|
-
return [];
|
|
69
|
-
}
|
|
70
|
-
return result.values.map((values) => {
|
|
71
|
-
const row = {};
|
|
72
|
-
result.columns.forEach((column, index) => {
|
|
73
|
-
row[column] = values[index] ?? null;
|
|
74
|
-
});
|
|
75
|
-
return row;
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
const DIRECT_SEARCH_MAX_WORKFLOW_FILES = 200;
|
|
79
|
-
function searchCapabilities(fts5Available) {
|
|
80
|
-
return {
|
|
81
|
-
backend: fts5Available ? SEARCH_BACKEND_FTS5 : SEARCH_BACKEND_TABLE_SCAN,
|
|
82
|
-
fts5Available,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
function detectLocalSearchCapabilities(database) {
|
|
86
|
-
if (process.env[TEST_DISABLE_FTS5_ENV] === '1') {
|
|
87
|
-
return searchCapabilities(false);
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
database.run('CREATE VIRTUAL TABLE temp.mustflow_fts5_probe USING fts5(value)');
|
|
91
|
-
database.run('DROP TABLE temp.mustflow_fts5_probe');
|
|
92
|
-
return searchCapabilities(true);
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return searchCapabilities(false);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
function joinedList(values) {
|
|
99
|
-
return [...values].sort((left, right) => left.localeCompare(right)).join(', ');
|
|
100
|
-
}
|
|
101
|
-
function readMetadataValue(database, key) {
|
|
102
|
-
return toSearchString(queryRows(database, 'SELECT value FROM metadata WHERE key = ?', [key])[0]?.value) || undefined;
|
|
103
|
-
}
|
|
104
|
-
function hasTable(database, tableName) {
|
|
105
|
-
return queryRows(database, 'SELECT name FROM sqlite_master WHERE type = "table" AND name = ?', [tableName]).length > 0;
|
|
106
|
-
}
|
|
107
|
-
function readStoredSearchCapabilities(database) {
|
|
108
|
-
const fts5Available = readMetadataValue(database, 'search_fts5_available') === 'true';
|
|
109
|
-
const backend = readMetadataValue(database, 'search_backend');
|
|
110
|
-
if (backend === SEARCH_BACKEND_FTS5 && hasTable(database, 'search_documents_fts')) {
|
|
111
|
-
return { backend: SEARCH_BACKEND_FTS5, fts5Available };
|
|
112
|
-
}
|
|
113
|
-
return { backend: SEARCH_BACKEND_TABLE_SCAN, fts5Available };
|
|
114
|
-
}
|
|
115
|
-
function toNullableNumber(value) {
|
|
116
|
-
if (typeof value !== 'number') {
|
|
117
|
-
return null;
|
|
118
|
-
}
|
|
119
|
-
return Number.isFinite(value) ? value : null;
|
|
120
|
-
}
|
|
121
|
-
function splitIndexedList(value) {
|
|
122
|
-
return toSearchString(value)
|
|
123
|
-
.split(',')
|
|
124
|
-
.map((item) => item.trim())
|
|
125
|
-
.filter(Boolean)
|
|
126
|
-
.sort((left, right) => left.localeCompare(right));
|
|
127
|
-
}
|
|
128
|
-
function createCommandEffectGraphStatus(databasePath, status, stalePaths = []) {
|
|
129
|
-
return {
|
|
130
|
-
source: 'local_index',
|
|
131
|
-
authority: 'explanation_only',
|
|
132
|
-
commandAuthority: '.mustflow/config/commands.toml',
|
|
133
|
-
grantsCommandAuthority: false,
|
|
134
|
-
status,
|
|
135
|
-
databasePath,
|
|
136
|
-
indexFresh: status === 'fresh',
|
|
137
|
-
stalePaths,
|
|
138
|
-
writeLocks: [],
|
|
139
|
-
lockConflicts: [],
|
|
140
|
-
refreshHint: status === 'fresh' ? null : 'Run `mf index` to refresh command-effect graph explanations.',
|
|
141
|
-
};
|
|
142
|
-
}
|
|
16
|
+
import { collectDocuments, collectSkillRoutes, collectSkills, } from './workflow-documents.js';
|
|
17
|
+
export { readLocalCommandEffectGraph, readLocalCommandEffectGraphs } from './effect-graph-read-model.js';
|
|
18
|
+
export { getLocalIndexDatabasePath } from './database-path.js';
|
|
19
|
+
export { readLocalPathSurface, readLocalPathSurfaces } from './path-surface-read-model.js';
|
|
20
|
+
export { searchLocalIndex } from './search-read-model.js';
|
|
143
21
|
async function readPreviousSourceAnchorSnapshots(databasePath) {
|
|
144
22
|
if (!existsSync(databasePath)) {
|
|
145
23
|
return [];
|
|
@@ -217,1028 +95,6 @@ JOIN source_anchor_fingerprints ON source_anchor_fingerprints.anchor_id = source
|
|
|
217
95
|
database.close();
|
|
218
96
|
}
|
|
219
97
|
}
|
|
220
|
-
function readCacheLayerSets(projectRoot) {
|
|
221
|
-
const mustflow = readMustflowToml(projectRoot);
|
|
222
|
-
const promptCache = readNestedTable(mustflow, 'prompt_cache');
|
|
223
|
-
const layers = readNestedTable(promptCache, 'layers');
|
|
224
|
-
const stable = readNestedTable(layers, 'stable');
|
|
225
|
-
const task = readNestedTable(layers, 'task');
|
|
226
|
-
const volatile = readNestedTable(layers, 'volatile');
|
|
227
|
-
const normalize = (values) => new Set(values.map((value) => toPosixPath(value)));
|
|
228
|
-
return {
|
|
229
|
-
stable: normalize(readOptionalStringArray(stable, 'read') ?? [...DEFAULT_PROMPT_CACHE_STABLE_READ]),
|
|
230
|
-
task: normalize(readOptionalStringArray(task, 'sources') ?? [...DEFAULT_PROMPT_CACHE_TASK_SOURCES]),
|
|
231
|
-
volatile: normalize(readOptionalStringArray(volatile, 'sources') ?? [...DEFAULT_PROMPT_CACHE_VOLATILE_SOURCES]),
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
function inferCacheLayer(relativePath, kind, cacheLayers) {
|
|
235
|
-
const normalized = relativePath ? toPosixPath(relativePath) : null;
|
|
236
|
-
if (normalized && cacheLayers.volatile.has(normalized)) {
|
|
237
|
-
return 'volatile';
|
|
238
|
-
}
|
|
239
|
-
if (normalized && cacheLayers.stable.has(normalized)) {
|
|
240
|
-
return 'stable';
|
|
241
|
-
}
|
|
242
|
-
if (kind === 'command_intent' && cacheLayers.stable.has('.mustflow/config/commands.toml')) {
|
|
243
|
-
return 'stable';
|
|
244
|
-
}
|
|
245
|
-
if (normalized &&
|
|
246
|
-
(cacheLayers.task.has(normalized) || normalized.startsWith('.mustflow/context/') || normalized.endsWith('/SKILL.md'))) {
|
|
247
|
-
return 'task';
|
|
248
|
-
}
|
|
249
|
-
if (normalized?.startsWith('.mustflow/state/') || normalized?.startsWith('.mustflow/cache/')) {
|
|
250
|
-
return 'volatile';
|
|
251
|
-
}
|
|
252
|
-
return 'task';
|
|
253
|
-
}
|
|
254
|
-
function withCacheHint(item, cacheLayers) {
|
|
255
|
-
const layer = inferCacheLayer(item.path ?? null, item.kind, cacheLayers);
|
|
256
|
-
return {
|
|
257
|
-
...item,
|
|
258
|
-
cache_layer: layer,
|
|
259
|
-
volatile: layer === 'volatile',
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
function workflowAuthorityForDocument(documentType) {
|
|
263
|
-
if (documentType === 'agent_rules' || documentType === 'config' || documentType === 'workflow_doc') {
|
|
264
|
-
return {
|
|
265
|
-
authority_rank: 2,
|
|
266
|
-
authority_label: 'workflow_authority',
|
|
267
|
-
source_scope: 'workflow',
|
|
268
|
-
navigation_only: false,
|
|
269
|
-
can_instruct_agent: true,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
return {
|
|
273
|
-
authority_rank: 4,
|
|
274
|
-
authority_label: 'workflow_context',
|
|
275
|
-
source_scope: 'workflow',
|
|
276
|
-
navigation_only: false,
|
|
277
|
-
can_instruct_agent: false,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
function skillAuthority() {
|
|
281
|
-
return {
|
|
282
|
-
authority_rank: 3,
|
|
283
|
-
authority_label: 'skill_procedure',
|
|
284
|
-
source_scope: 'workflow',
|
|
285
|
-
navigation_only: false,
|
|
286
|
-
can_instruct_agent: true,
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
function commandIntentAuthority() {
|
|
290
|
-
return {
|
|
291
|
-
authority_rank: 1,
|
|
292
|
-
authority_label: 'command_contract',
|
|
293
|
-
source_scope: 'workflow',
|
|
294
|
-
navigation_only: false,
|
|
295
|
-
can_instruct_agent: true,
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
function sourceAnchorAuthority() {
|
|
299
|
-
return {
|
|
300
|
-
authority_rank: 5,
|
|
301
|
-
authority_label: 'source_navigation_hint',
|
|
302
|
-
source_scope: 'source',
|
|
303
|
-
navigation_only: true,
|
|
304
|
-
can_instruct_agent: false,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
function getMatchSnippet(fields, query) {
|
|
308
|
-
const normalized = normalizeSearchText(fields.join(' '));
|
|
309
|
-
const lower = normalized.toLowerCase();
|
|
310
|
-
let start = lower.indexOf(query.toLowerCase());
|
|
311
|
-
let matchLength = query.length;
|
|
312
|
-
if (start === -1) {
|
|
313
|
-
const [firstGram] = buildSearchNgrams([query]).filter((gram) => lower.includes(gram));
|
|
314
|
-
if (!firstGram) {
|
|
315
|
-
return truncateSearchMatchSnippet(normalized);
|
|
316
|
-
}
|
|
317
|
-
start = lower.indexOf(firstGram);
|
|
318
|
-
matchLength = firstGram.length;
|
|
319
|
-
}
|
|
320
|
-
const from = Math.max(0, start - SEARCH_MATCH_CONTEXT_BEFORE_CHARS);
|
|
321
|
-
const to = Math.min(normalized.length, start + matchLength + SEARCH_MATCH_CONTEXT_AFTER_CHARS);
|
|
322
|
-
const prefix = from > 0 ? SEARCH_MATCH_TRUNCATION_MARKER : '';
|
|
323
|
-
const suffix = to < normalized.length ? SEARCH_MATCH_TRUNCATION_MARKER : '';
|
|
324
|
-
return truncateSearchMatchSnippet(`${prefix}${normalized.slice(from, to)}${suffix}`);
|
|
325
|
-
}
|
|
326
|
-
function truncateSearchMatchSnippet(value) {
|
|
327
|
-
if (value.length <= MAX_SEARCH_MATCH_SNIPPET_CHARS) {
|
|
328
|
-
return value;
|
|
329
|
-
}
|
|
330
|
-
return `${value.slice(0, MAX_SEARCH_MATCH_SNIPPET_CHARS - SEARCH_MATCH_TRUNCATION_MARKER.length)}${SEARCH_MATCH_TRUNCATION_MARKER}`;
|
|
331
|
-
}
|
|
332
|
-
function scoreMatch(primaryFields, secondaryFields, query) {
|
|
333
|
-
const lowerQuery = query.toLowerCase();
|
|
334
|
-
if (primaryFields.some((field) => field.toLowerCase() === lowerQuery)) {
|
|
335
|
-
return 100;
|
|
336
|
-
}
|
|
337
|
-
if (primaryFields.some((field) => field.toLowerCase().includes(lowerQuery))) {
|
|
338
|
-
return 80;
|
|
339
|
-
}
|
|
340
|
-
if (secondaryFields.some((field) => field.toLowerCase().includes(lowerQuery))) {
|
|
341
|
-
return 40;
|
|
342
|
-
}
|
|
343
|
-
return 0;
|
|
344
|
-
}
|
|
345
|
-
function isMatched(fields, query) {
|
|
346
|
-
const lowerQuery = query.toLowerCase();
|
|
347
|
-
return fields.some((field) => field.toLowerCase().includes(lowerQuery));
|
|
348
|
-
}
|
|
349
|
-
function createSchema(database, capabilities) {
|
|
350
|
-
database.run(`
|
|
351
|
-
CREATE TABLE metadata (
|
|
352
|
-
key TEXT PRIMARY KEY,
|
|
353
|
-
value TEXT NOT NULL
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
CREATE TABLE indexed_files (
|
|
357
|
-
path TEXT PRIMARY KEY,
|
|
358
|
-
source_scope TEXT NOT NULL,
|
|
359
|
-
size_bytes INTEGER NOT NULL,
|
|
360
|
-
mtime_ms INTEGER NOT NULL,
|
|
361
|
-
content_hash TEXT NOT NULL,
|
|
362
|
-
indexed_at TEXT NOT NULL,
|
|
363
|
-
index_mode TEXT NOT NULL,
|
|
364
|
-
parser_version TEXT NOT NULL
|
|
365
|
-
);
|
|
366
|
-
|
|
367
|
-
CREATE TABLE documents (
|
|
368
|
-
path TEXT PRIMARY KEY,
|
|
369
|
-
type TEXT NOT NULL,
|
|
370
|
-
title TEXT NOT NULL,
|
|
371
|
-
locale TEXT,
|
|
372
|
-
revision INTEGER,
|
|
373
|
-
content_hash TEXT NOT NULL,
|
|
374
|
-
content_snippet TEXT NOT NULL
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
CREATE TABLE sections (
|
|
378
|
-
document_path TEXT NOT NULL,
|
|
379
|
-
ordinal INTEGER NOT NULL,
|
|
380
|
-
heading TEXT NOT NULL,
|
|
381
|
-
PRIMARY KEY (document_path, ordinal)
|
|
382
|
-
);
|
|
383
|
-
|
|
384
|
-
CREATE TABLE document_terms (
|
|
385
|
-
document_path TEXT NOT NULL,
|
|
386
|
-
term TEXT NOT NULL,
|
|
387
|
-
source TEXT NOT NULL,
|
|
388
|
-
PRIMARY KEY (document_path, term, source)
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
CREATE TABLE search_ngrams (
|
|
392
|
-
target_kind TEXT NOT NULL,
|
|
393
|
-
target_key TEXT NOT NULL,
|
|
394
|
-
gram TEXT NOT NULL,
|
|
395
|
-
source TEXT NOT NULL,
|
|
396
|
-
PRIMARY KEY (target_kind, target_key, gram, source)
|
|
397
|
-
);
|
|
398
|
-
|
|
399
|
-
CREATE INDEX search_ngrams_lookup ON search_ngrams(target_kind, gram, target_key);
|
|
400
|
-
|
|
401
|
-
CREATE TABLE skills (
|
|
402
|
-
name TEXT PRIMARY KEY,
|
|
403
|
-
path TEXT NOT NULL,
|
|
404
|
-
title TEXT NOT NULL
|
|
405
|
-
);
|
|
406
|
-
|
|
407
|
-
CREATE TABLE skill_routes (
|
|
408
|
-
skill_name TEXT NOT NULL,
|
|
409
|
-
skill_path TEXT NOT NULL,
|
|
410
|
-
trigger TEXT NOT NULL,
|
|
411
|
-
required_input TEXT NOT NULL,
|
|
412
|
-
edit_scope TEXT NOT NULL,
|
|
413
|
-
risk TEXT NOT NULL,
|
|
414
|
-
verification_intents TEXT NOT NULL,
|
|
415
|
-
expected_output TEXT NOT NULL,
|
|
416
|
-
PRIMARY KEY (skill_name, trigger)
|
|
417
|
-
);
|
|
418
|
-
|
|
419
|
-
CREATE TABLE command_intents (
|
|
420
|
-
name TEXT PRIMARY KEY,
|
|
421
|
-
status TEXT NOT NULL,
|
|
422
|
-
lifecycle TEXT,
|
|
423
|
-
run_policy TEXT,
|
|
424
|
-
description TEXT
|
|
425
|
-
);
|
|
426
|
-
|
|
427
|
-
CREATE TABLE command_effects (
|
|
428
|
-
intent TEXT NOT NULL,
|
|
429
|
-
source TEXT NOT NULL,
|
|
430
|
-
access TEXT NOT NULL,
|
|
431
|
-
mode TEXT NOT NULL,
|
|
432
|
-
path TEXT,
|
|
433
|
-
lock TEXT NOT NULL,
|
|
434
|
-
concurrency TEXT NOT NULL
|
|
435
|
-
);
|
|
436
|
-
|
|
437
|
-
CREATE VIEW command_write_locks AS
|
|
438
|
-
SELECT
|
|
439
|
-
intent,
|
|
440
|
-
lock,
|
|
441
|
-
group_concat(DISTINCT path) AS paths,
|
|
442
|
-
group_concat(DISTINCT mode) AS modes,
|
|
443
|
-
group_concat(DISTINCT source) AS sources,
|
|
444
|
-
group_concat(DISTINCT concurrency) AS concurrencies,
|
|
445
|
-
count(*) AS effect_count
|
|
446
|
-
FROM command_effects
|
|
447
|
-
WHERE access = 'write'
|
|
448
|
-
GROUP BY intent, lock;
|
|
449
|
-
|
|
450
|
-
CREATE VIEW command_lock_conflicts AS
|
|
451
|
-
SELECT
|
|
452
|
-
a.intent AS left_intent,
|
|
453
|
-
b.intent AS right_intent,
|
|
454
|
-
a.lock AS lock,
|
|
455
|
-
group_concat(DISTINCT a.path) AS left_paths,
|
|
456
|
-
group_concat(DISTINCT b.path) AS right_paths,
|
|
457
|
-
group_concat(DISTINCT a.mode) AS left_modes,
|
|
458
|
-
group_concat(DISTINCT b.mode) AS right_modes,
|
|
459
|
-
group_concat(DISTINCT a.concurrency) AS left_concurrencies,
|
|
460
|
-
group_concat(DISTINCT b.concurrency) AS right_concurrencies
|
|
461
|
-
FROM command_effects a
|
|
462
|
-
JOIN command_effects b
|
|
463
|
-
ON a.lock = b.lock
|
|
464
|
-
AND a.intent < b.intent
|
|
465
|
-
WHERE
|
|
466
|
-
a.access = 'write'
|
|
467
|
-
OR b.access = 'write'
|
|
468
|
-
OR a.concurrency = 'exclusive'
|
|
469
|
-
OR b.concurrency = 'exclusive'
|
|
470
|
-
OR a.mode = 'delete_recreate'
|
|
471
|
-
OR b.mode = 'delete_recreate'
|
|
472
|
-
GROUP BY a.intent, b.intent, a.lock;
|
|
473
|
-
|
|
474
|
-
CREATE TABLE path_surfaces (
|
|
475
|
-
rule_id TEXT PRIMARY KEY,
|
|
476
|
-
pattern_kind TEXT NOT NULL,
|
|
477
|
-
pattern TEXT NOT NULL,
|
|
478
|
-
pattern_flags TEXT NOT NULL,
|
|
479
|
-
surface_kind TEXT NOT NULL,
|
|
480
|
-
category TEXT NOT NULL,
|
|
481
|
-
is_public_surface INTEGER NOT NULL,
|
|
482
|
-
update_policy TEXT NOT NULL
|
|
483
|
-
);
|
|
484
|
-
|
|
485
|
-
CREATE TABLE path_surface_reasons (
|
|
486
|
-
rule_id TEXT NOT NULL,
|
|
487
|
-
reason_kind TEXT NOT NULL,
|
|
488
|
-
reason TEXT NOT NULL,
|
|
489
|
-
ordinal INTEGER NOT NULL,
|
|
490
|
-
PRIMARY KEY (rule_id, reason_kind, reason)
|
|
491
|
-
);
|
|
492
|
-
|
|
493
|
-
CREATE TABLE source_anchors (
|
|
494
|
-
id TEXT PRIMARY KEY,
|
|
495
|
-
path TEXT NOT NULL,
|
|
496
|
-
line_start INTEGER NOT NULL,
|
|
497
|
-
purpose TEXT,
|
|
498
|
-
search_terms TEXT NOT NULL,
|
|
499
|
-
invariant TEXT,
|
|
500
|
-
risk TEXT NOT NULL,
|
|
501
|
-
navigation_only INTEGER NOT NULL,
|
|
502
|
-
can_instruct_agent INTEGER NOT NULL
|
|
503
|
-
);
|
|
504
|
-
|
|
505
|
-
CREATE TABLE source_anchor_fingerprints (
|
|
506
|
-
anchor_id TEXT PRIMARY KEY,
|
|
507
|
-
path TEXT NOT NULL,
|
|
508
|
-
line_start INTEGER NOT NULL,
|
|
509
|
-
anchor_metadata_hash TEXT NOT NULL,
|
|
510
|
-
anchor_text_hash TEXT NOT NULL,
|
|
511
|
-
context_hash TEXT NOT NULL,
|
|
512
|
-
search_terms_hash TEXT,
|
|
513
|
-
invariant_hash TEXT,
|
|
514
|
-
risk_hash TEXT NOT NULL,
|
|
515
|
-
symbol_kind TEXT NOT NULL,
|
|
516
|
-
symbol_name TEXT,
|
|
517
|
-
symbol_exported INTEGER NOT NULL,
|
|
518
|
-
signature_hash TEXT,
|
|
519
|
-
body_hash TEXT,
|
|
520
|
-
symbol_start_line INTEGER,
|
|
521
|
-
symbol_end_line INTEGER
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
CREATE TABLE source_anchor_status (
|
|
525
|
-
anchor_id TEXT PRIMARY KEY,
|
|
526
|
-
status TEXT NOT NULL,
|
|
527
|
-
confidence REAL NOT NULL,
|
|
528
|
-
identity_signal TEXT NOT NULL,
|
|
529
|
-
location_signal TEXT NOT NULL,
|
|
530
|
-
symbol_signal TEXT NOT NULL,
|
|
531
|
-
body_signal TEXT NOT NULL,
|
|
532
|
-
metadata_signal TEXT NOT NULL,
|
|
533
|
-
semantic_signal TEXT NOT NULL,
|
|
534
|
-
risk_signal TEXT NOT NULL,
|
|
535
|
-
navigation_only INTEGER NOT NULL,
|
|
536
|
-
can_instruct_agent INTEGER NOT NULL
|
|
537
|
-
);
|
|
538
|
-
|
|
539
|
-
CREATE TABLE verification_evidence_summaries (
|
|
540
|
-
source_path TEXT PRIMARY KEY,
|
|
541
|
-
source_hash TEXT NOT NULL,
|
|
542
|
-
command TEXT NOT NULL,
|
|
543
|
-
kind TEXT NOT NULL,
|
|
544
|
-
status TEXT NOT NULL,
|
|
545
|
-
run_dir TEXT,
|
|
546
|
-
manifest_path TEXT,
|
|
547
|
-
verification_plan_id TEXT,
|
|
548
|
-
completion_status TEXT,
|
|
549
|
-
primary_reason TEXT,
|
|
550
|
-
matched_intents INTEGER NOT NULL,
|
|
551
|
-
ran_intents INTEGER NOT NULL,
|
|
552
|
-
passed_intents INTEGER NOT NULL,
|
|
553
|
-
failed_intents INTEGER NOT NULL,
|
|
554
|
-
skipped_intents INTEGER NOT NULL,
|
|
555
|
-
receipt_count INTEGER NOT NULL,
|
|
556
|
-
coverage_count INTEGER NOT NULL,
|
|
557
|
-
remaining_risk_count INTEGER NOT NULL,
|
|
558
|
-
failure_fingerprint TEXT
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
CREATE TABLE verification_plans (
|
|
562
|
-
plan_id TEXT PRIMARY KEY,
|
|
563
|
-
source_path TEXT NOT NULL,
|
|
564
|
-
classification_hash TEXT,
|
|
565
|
-
command_contract_hash TEXT,
|
|
566
|
-
selected_intents_hash TEXT,
|
|
567
|
-
created_at TEXT,
|
|
568
|
-
source_hash TEXT NOT NULL
|
|
569
|
-
);
|
|
570
|
-
|
|
571
|
-
CREATE TABLE acceptance_criteria (
|
|
572
|
-
criterion_id TEXT NOT NULL,
|
|
573
|
-
plan_id TEXT NOT NULL,
|
|
574
|
-
source TEXT NOT NULL,
|
|
575
|
-
statement_hash TEXT,
|
|
576
|
-
reason TEXT,
|
|
577
|
-
surface TEXT,
|
|
578
|
-
path_hash TEXT,
|
|
579
|
-
PRIMARY KEY (plan_id, criterion_id)
|
|
580
|
-
);
|
|
581
|
-
|
|
582
|
-
CREATE TABLE criterion_coverage (
|
|
583
|
-
criterion_id TEXT NOT NULL,
|
|
584
|
-
plan_id TEXT NOT NULL,
|
|
585
|
-
status TEXT NOT NULL,
|
|
586
|
-
receipt_count INTEGER NOT NULL,
|
|
587
|
-
gap_count INTEGER NOT NULL,
|
|
588
|
-
risk_count INTEGER NOT NULL,
|
|
589
|
-
PRIMARY KEY (plan_id, criterion_id)
|
|
590
|
-
);
|
|
591
|
-
|
|
592
|
-
CREATE TABLE verification_receipt_summaries (
|
|
593
|
-
source_path TEXT NOT NULL,
|
|
594
|
-
ordinal INTEGER NOT NULL,
|
|
595
|
-
intent TEXT,
|
|
596
|
-
status TEXT NOT NULL,
|
|
597
|
-
skipped INTEGER NOT NULL,
|
|
598
|
-
verification_plan_id TEXT,
|
|
599
|
-
receipt_path TEXT,
|
|
600
|
-
receipt_sha256 TEXT,
|
|
601
|
-
PRIMARY KEY (source_path, ordinal)
|
|
602
|
-
);
|
|
603
|
-
|
|
604
|
-
CREATE TABLE command_receipt_summaries (
|
|
605
|
-
receipt_hash TEXT NOT NULL,
|
|
606
|
-
plan_id TEXT NOT NULL,
|
|
607
|
-
intent TEXT,
|
|
608
|
-
status TEXT NOT NULL,
|
|
609
|
-
command_fingerprint TEXT,
|
|
610
|
-
contract_fingerprint TEXT,
|
|
611
|
-
current_state_hash TEXT,
|
|
612
|
-
write_drift_status TEXT,
|
|
613
|
-
PRIMARY KEY (plan_id, receipt_hash)
|
|
614
|
-
);
|
|
615
|
-
|
|
616
|
-
CREATE TABLE verification_coverage_states (
|
|
617
|
-
source_path TEXT NOT NULL,
|
|
618
|
-
criterion_id TEXT NOT NULL,
|
|
619
|
-
source TEXT NOT NULL,
|
|
620
|
-
status TEXT NOT NULL,
|
|
621
|
-
requirement_reason TEXT,
|
|
622
|
-
intents TEXT NOT NULL,
|
|
623
|
-
receipt_count INTEGER NOT NULL,
|
|
624
|
-
gap_count INTEGER NOT NULL,
|
|
625
|
-
source_anchor_count INTEGER NOT NULL,
|
|
626
|
-
PRIMARY KEY (source_path, criterion_id)
|
|
627
|
-
);
|
|
628
|
-
|
|
629
|
-
CREATE TABLE verification_risk_signals (
|
|
630
|
-
source_path TEXT NOT NULL,
|
|
631
|
-
ordinal INTEGER NOT NULL,
|
|
632
|
-
code TEXT NOT NULL,
|
|
633
|
-
severity TEXT NOT NULL,
|
|
634
|
-
detail_hash TEXT NOT NULL,
|
|
635
|
-
PRIMARY KEY (source_path, ordinal)
|
|
636
|
-
);
|
|
637
|
-
|
|
638
|
-
CREATE TABLE validation_ratchet_signals (
|
|
639
|
-
signal_id TEXT PRIMARY KEY,
|
|
640
|
-
plan_id TEXT,
|
|
641
|
-
code TEXT NOT NULL,
|
|
642
|
-
severity TEXT NOT NULL,
|
|
643
|
-
path_hash TEXT NOT NULL,
|
|
644
|
-
before_hash TEXT,
|
|
645
|
-
after_hash TEXT
|
|
646
|
-
);
|
|
647
|
-
|
|
648
|
-
CREATE TABLE completion_verdict_summaries (
|
|
649
|
-
claim_id TEXT PRIMARY KEY,
|
|
650
|
-
plan_id TEXT NOT NULL,
|
|
651
|
-
status TEXT NOT NULL,
|
|
652
|
-
primary_reason TEXT,
|
|
653
|
-
risk_count INTEGER NOT NULL,
|
|
654
|
-
contradiction_count INTEGER NOT NULL,
|
|
655
|
-
blocker_count INTEGER NOT NULL
|
|
656
|
-
);
|
|
657
|
-
|
|
658
|
-
CREATE TABLE repro_routes (
|
|
659
|
-
route_id TEXT PRIMARY KEY,
|
|
660
|
-
task_hash TEXT NOT NULL,
|
|
661
|
-
route_digest TEXT,
|
|
662
|
-
route_kind TEXT,
|
|
663
|
-
failure_oracle_hash TEXT
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
CREATE TABLE repro_observations (
|
|
667
|
-
route_id TEXT NOT NULL,
|
|
668
|
-
phase TEXT NOT NULL,
|
|
669
|
-
outcome TEXT,
|
|
670
|
-
receipt_hash TEXT,
|
|
671
|
-
diagnostic_fingerprint TEXT NOT NULL,
|
|
672
|
-
PRIMARY KEY (route_id, phase)
|
|
673
|
-
);
|
|
674
|
-
|
|
675
|
-
CREATE TABLE failure_fingerprints (
|
|
676
|
-
fingerprint TEXT PRIMARY KEY,
|
|
677
|
-
plan_id TEXT,
|
|
678
|
-
failed_intents_hash TEXT,
|
|
679
|
-
risk_codes_hash TEXT,
|
|
680
|
-
seen_count INTEGER NOT NULL,
|
|
681
|
-
first_seen_at TEXT,
|
|
682
|
-
last_seen_at TEXT
|
|
683
|
-
);
|
|
684
|
-
|
|
685
|
-
CREATE TABLE verification_failure_fingerprints (
|
|
686
|
-
source_path TEXT NOT NULL,
|
|
687
|
-
fingerprint TEXT NOT NULL,
|
|
688
|
-
verification_plan_id TEXT,
|
|
689
|
-
status TEXT NOT NULL,
|
|
690
|
-
failed_intents TEXT NOT NULL,
|
|
691
|
-
primary_reason TEXT,
|
|
692
|
-
failed_intents_hash TEXT,
|
|
693
|
-
risk_codes_hash TEXT,
|
|
694
|
-
affected_surfaces_hash TEXT,
|
|
695
|
-
first_seen_at TEXT,
|
|
696
|
-
last_seen_at TEXT,
|
|
697
|
-
seen_count INTEGER NOT NULL,
|
|
698
|
-
requires_new_evidence INTEGER NOT NULL,
|
|
699
|
-
PRIMARY KEY (source_path, fingerprint)
|
|
700
|
-
);
|
|
701
|
-
|
|
702
|
-
CREATE TABLE source_anchor_risk_signals (
|
|
703
|
-
anchor_id TEXT PRIMARY KEY,
|
|
704
|
-
path_hash TEXT NOT NULL,
|
|
705
|
-
status TEXT NOT NULL,
|
|
706
|
-
risk_signal TEXT NOT NULL,
|
|
707
|
-
confidence REAL NOT NULL,
|
|
708
|
-
navigation_only INTEGER NOT NULL,
|
|
709
|
-
can_instruct_agent INTEGER NOT NULL
|
|
710
|
-
);
|
|
711
|
-
`);
|
|
712
|
-
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
713
|
-
database.run(`
|
|
714
|
-
CREATE VIRTUAL TABLE search_documents_fts USING fts5(
|
|
715
|
-
path UNINDEXED,
|
|
716
|
-
type UNINDEXED,
|
|
717
|
-
title,
|
|
718
|
-
sections,
|
|
719
|
-
terms,
|
|
720
|
-
snippet
|
|
721
|
-
);
|
|
722
|
-
|
|
723
|
-
CREATE VIRTUAL TABLE search_skills_fts USING fts5(
|
|
724
|
-
name UNINDEXED,
|
|
725
|
-
path UNINDEXED,
|
|
726
|
-
title
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
CREATE VIRTUAL TABLE search_skill_routes_fts USING fts5(
|
|
730
|
-
route_key UNINDEXED,
|
|
731
|
-
skill_name UNINDEXED,
|
|
732
|
-
skill_path UNINDEXED,
|
|
733
|
-
trigger,
|
|
734
|
-
required_input,
|
|
735
|
-
edit_scope,
|
|
736
|
-
risk,
|
|
737
|
-
verification_intents,
|
|
738
|
-
expected_output
|
|
739
|
-
);
|
|
740
|
-
|
|
741
|
-
CREATE VIRTUAL TABLE search_command_intents_fts USING fts5(
|
|
742
|
-
name UNINDEXED,
|
|
743
|
-
status UNINDEXED,
|
|
744
|
-
lifecycle UNINDEXED,
|
|
745
|
-
run_policy UNINDEXED,
|
|
746
|
-
description,
|
|
747
|
-
effects
|
|
748
|
-
);
|
|
749
|
-
|
|
750
|
-
CREATE VIRTUAL TABLE search_source_anchors_fts USING fts5(
|
|
751
|
-
id UNINDEXED,
|
|
752
|
-
path UNINDEXED,
|
|
753
|
-
purpose,
|
|
754
|
-
search_terms,
|
|
755
|
-
invariant,
|
|
756
|
-
risk
|
|
757
|
-
);
|
|
758
|
-
`);
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
function insertDocumentTerm(database, documentPath, term, source) {
|
|
762
|
-
const normalized = normalizeSearchText(term ?? '');
|
|
763
|
-
if (normalized.length === 0) {
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
database.run('INSERT OR IGNORE INTO document_terms (document_path, term, source) VALUES (?, ?, ?)', [
|
|
767
|
-
documentPath,
|
|
768
|
-
normalized,
|
|
769
|
-
source,
|
|
770
|
-
]);
|
|
771
|
-
}
|
|
772
|
-
function insertSearchNgrams(database, targetKind, targetKey, values, source) {
|
|
773
|
-
for (const gram of buildSearchNgrams(values)) {
|
|
774
|
-
database.run('INSERT OR IGNORE INTO search_ngrams (target_kind, target_key, gram, source) VALUES (?, ?, ?, ?)', [targetKind, targetKey, gram, source]);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
function insertPathSurfaceReasons(database, ruleId, reasonKind, values) {
|
|
778
|
-
values.forEach((value, index) => {
|
|
779
|
-
database.run('INSERT INTO path_surface_reasons (rule_id, reason_kind, reason, ordinal) VALUES (?, ?, ?, ?)', [ruleId, reasonKind, value, index + 1]);
|
|
780
|
-
});
|
|
781
|
-
}
|
|
782
|
-
function populatePathSurfaceReadModel(database) {
|
|
783
|
-
for (const rule of listChangeClassificationRuleDescriptors()) {
|
|
784
|
-
database.run('INSERT INTO path_surfaces (rule_id, pattern_kind, pattern, pattern_flags, surface_kind, category, is_public_surface, update_policy) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
785
|
-
rule.id,
|
|
786
|
-
rule.patternKind,
|
|
787
|
-
rule.pattern,
|
|
788
|
-
rule.patternFlags,
|
|
789
|
-
rule.surface.kind,
|
|
790
|
-
rule.surface.category,
|
|
791
|
-
rule.surface.isPublicSurface ? 1 : 0,
|
|
792
|
-
rule.surface.updatePolicy,
|
|
793
|
-
]);
|
|
794
|
-
insertPathSurfaceReasons(database, rule.id, 'change_kind', rule.changeKinds);
|
|
795
|
-
insertPathSurfaceReasons(database, rule.id, 'validation_reason', rule.surface.validationReasons);
|
|
796
|
-
insertPathSurfaceReasons(database, rule.id, 'affected_contract', rule.surface.affectedContracts);
|
|
797
|
-
insertPathSurfaceReasons(database, rule.id, 'drift_check', rule.surface.driftChecks);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
function populateSearchTables(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors) {
|
|
801
|
-
for (const document of documents) {
|
|
802
|
-
const documentTerms = queryRows(database, 'SELECT term FROM document_terms WHERE document_path = ? ORDER BY term', [
|
|
803
|
-
document.path,
|
|
804
|
-
]).map((row) => toSearchString(row.term));
|
|
805
|
-
insertSearchNgrams(database, 'document', document.path, [
|
|
806
|
-
document.path,
|
|
807
|
-
document.type,
|
|
808
|
-
document.title,
|
|
809
|
-
document.sections.join(' '),
|
|
810
|
-
documentTerms.join(' '),
|
|
811
|
-
document.contentSnippet,
|
|
812
|
-
], 'workflow_document');
|
|
813
|
-
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
814
|
-
database.run('INSERT INTO search_documents_fts (path, type, title, sections, terms, snippet) VALUES (?, ?, ?, ?, ?, ?)', [
|
|
815
|
-
document.path,
|
|
816
|
-
document.type,
|
|
817
|
-
document.title,
|
|
818
|
-
document.sections.join(' '),
|
|
819
|
-
documentTerms.join(' '),
|
|
820
|
-
document.contentSnippet,
|
|
821
|
-
]);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
for (const skill of skills) {
|
|
825
|
-
insertSearchNgrams(database, 'skill', skill.name, [skill.name, skill.path, skill.title], 'skill');
|
|
826
|
-
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
827
|
-
database.run('INSERT INTO search_skills_fts (name, path, title) VALUES (?, ?, ?)', [
|
|
828
|
-
skill.name,
|
|
829
|
-
skill.path,
|
|
830
|
-
skill.title,
|
|
831
|
-
]);
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
for (const route of skillRoutes) {
|
|
835
|
-
const verificationIntents = route.verificationIntents.join(' ');
|
|
836
|
-
insertSearchNgrams(database, 'skill_route', skillRouteKey(route), [
|
|
837
|
-
skillRouteKey(route),
|
|
838
|
-
route.skillName,
|
|
839
|
-
route.skillPath,
|
|
840
|
-
route.trigger,
|
|
841
|
-
route.requiredInput,
|
|
842
|
-
route.editScope,
|
|
843
|
-
route.risk,
|
|
844
|
-
verificationIntents,
|
|
845
|
-
route.expectedOutput,
|
|
846
|
-
], 'skill_route');
|
|
847
|
-
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
848
|
-
database.run('INSERT INTO search_skill_routes_fts (route_key, skill_name, skill_path, trigger, required_input, edit_scope, risk, verification_intents, expected_output) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
849
|
-
skillRouteKey(route),
|
|
850
|
-
route.skillName,
|
|
851
|
-
route.skillPath,
|
|
852
|
-
route.trigger,
|
|
853
|
-
route.requiredInput,
|
|
854
|
-
route.editScope,
|
|
855
|
-
route.risk,
|
|
856
|
-
verificationIntents,
|
|
857
|
-
route.expectedOutput,
|
|
858
|
-
]);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
for (const intent of commandIntents) {
|
|
862
|
-
const effects = intent.effects
|
|
863
|
-
.flatMap((effect) => [effect.lock, effect.path ?? '', effect.mode, effect.access, effect.concurrency])
|
|
864
|
-
.join(' ');
|
|
865
|
-
insertSearchNgrams(database, 'command_intent', intent.name, [intent.name, intent.status, intent.lifecycle ?? '', intent.runPolicy ?? '', intent.description ?? '', effects], 'command_intent');
|
|
866
|
-
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
867
|
-
database.run('INSERT INTO search_command_intents_fts (name, status, lifecycle, run_policy, description, effects) VALUES (?, ?, ?, ?, ?, ?)', [intent.name, intent.status, intent.lifecycle, intent.runPolicy, intent.description, effects]);
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
for (const anchor of sourceAnchors) {
|
|
871
|
-
insertSearchNgrams(database, 'source_anchor', anchor.id, [
|
|
872
|
-
anchor.id,
|
|
873
|
-
anchor.path,
|
|
874
|
-
anchor.purpose ?? '',
|
|
875
|
-
anchor.search.join(' '),
|
|
876
|
-
anchor.invariant ?? '',
|
|
877
|
-
anchor.risk.join(' '),
|
|
878
|
-
], 'source_anchor');
|
|
879
|
-
if (capabilities.backend === SEARCH_BACKEND_FTS5) {
|
|
880
|
-
database.run('INSERT INTO search_source_anchors_fts (id, path, purpose, search_terms, invariant, risk) VALUES (?, ?, ?, ?, ?, ?)', [
|
|
881
|
-
anchor.id,
|
|
882
|
-
anchor.path,
|
|
883
|
-
anchor.purpose,
|
|
884
|
-
anchor.search.join(' '),
|
|
885
|
-
anchor.invariant,
|
|
886
|
-
anchor.risk.join(' '),
|
|
887
|
-
]);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
function createSourceAnchorRiskSignals(sourceAnchors) {
|
|
892
|
-
return sourceAnchors
|
|
893
|
-
.filter((anchor) => ['changed', 'review', 'stale'].includes(anchor.status))
|
|
894
|
-
.map((anchor) => ({
|
|
895
|
-
anchorId: anchor.id,
|
|
896
|
-
pathHash: sha256Text(anchor.path),
|
|
897
|
-
status: anchor.status,
|
|
898
|
-
riskSignal: anchor.signals.risk,
|
|
899
|
-
confidence: anchor.confidence,
|
|
900
|
-
navigationOnly: anchor.navigationOnly,
|
|
901
|
-
canInstructAgent: anchor.canInstructAgent,
|
|
902
|
-
}));
|
|
903
|
-
}
|
|
904
|
-
function rollbackTransaction(database) {
|
|
905
|
-
try {
|
|
906
|
-
database.run('ROLLBACK');
|
|
907
|
-
}
|
|
908
|
-
catch {
|
|
909
|
-
// Keep the original indexing failure as the actionable error.
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
function populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, verificationEvidence, indexMode, sourceScopeHash, sourceIndexEnabled, indexedAt) {
|
|
913
|
-
const sourceAnchorRiskSignals = createSourceAnchorRiskSignals(sourceAnchors);
|
|
914
|
-
database.run('BEGIN TRANSACTION');
|
|
915
|
-
try {
|
|
916
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['schema_version', LOCAL_INDEX_SCHEMA_VERSION]);
|
|
917
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['parser_version', LOCAL_INDEX_PARSER_VERSION]);
|
|
918
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['content_mode', LOCAL_INDEX_CONTENT_MODE]);
|
|
919
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
920
|
-
'store_full_content',
|
|
921
|
-
String(LOCAL_INDEX_STORE_FULL_CONTENT),
|
|
922
|
-
]);
|
|
923
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
924
|
-
'max_snippet_bytes_per_document',
|
|
925
|
-
String(MAX_SNIPPET_BYTES_PER_DOCUMENT),
|
|
926
|
-
]);
|
|
927
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
928
|
-
'search_ngram_max_token_chars',
|
|
929
|
-
String(SEARCH_NGRAM_MAX_TOKEN_CHARS),
|
|
930
|
-
]);
|
|
931
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
932
|
-
'search_ngram_max_grams_per_target',
|
|
933
|
-
String(SEARCH_NGRAM_MAX_GRAMS_PER_TARGET),
|
|
934
|
-
]);
|
|
935
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
936
|
-
'source_index_max_file_bytes',
|
|
937
|
-
String(SOURCE_INDEX_MAX_FILE_BYTES),
|
|
938
|
-
]);
|
|
939
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
940
|
-
'excluded_raw_data_kinds',
|
|
941
|
-
LOCAL_INDEX_EXCLUDED_RAW_DATA_KINDS.join(','),
|
|
942
|
-
]);
|
|
943
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['search_backend', capabilities.backend]);
|
|
944
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
|
|
945
|
-
'search_fts5_available',
|
|
946
|
-
String(capabilities.fts5Available),
|
|
947
|
-
]);
|
|
948
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['source_scope_hash', sourceScopeHash]);
|
|
949
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['source_index_enabled', String(sourceIndexEnabled)]);
|
|
950
|
-
database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['index_mode', indexMode]);
|
|
951
|
-
for (const indexedFile of indexedFiles) {
|
|
952
|
-
database.run('INSERT INTO indexed_files (path, source_scope, size_bytes, mtime_ms, content_hash, indexed_at, index_mode, parser_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
953
|
-
indexedFile.path,
|
|
954
|
-
indexedFile.sourceScope,
|
|
955
|
-
indexedFile.sizeBytes,
|
|
956
|
-
indexedFile.mtimeMs,
|
|
957
|
-
indexedFile.contentHash,
|
|
958
|
-
indexedAt,
|
|
959
|
-
indexMode,
|
|
960
|
-
LOCAL_INDEX_PARSER_VERSION,
|
|
961
|
-
]);
|
|
962
|
-
}
|
|
963
|
-
for (const document of documents) {
|
|
964
|
-
database.run('INSERT INTO documents (path, type, title, locale, revision, content_hash, content_snippet) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
965
|
-
document.path,
|
|
966
|
-
document.type,
|
|
967
|
-
document.title,
|
|
968
|
-
document.locale,
|
|
969
|
-
document.revision,
|
|
970
|
-
document.contentHash,
|
|
971
|
-
document.contentSnippet,
|
|
972
|
-
]);
|
|
973
|
-
document.sections.forEach((heading, index) => {
|
|
974
|
-
database.run('INSERT INTO sections (document_path, ordinal, heading) VALUES (?, ?, ?)', [
|
|
975
|
-
document.path,
|
|
976
|
-
index + 1,
|
|
977
|
-
heading,
|
|
978
|
-
]);
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
for (const skill of skills) {
|
|
982
|
-
database.run('INSERT INTO skills (name, path, title) VALUES (?, ?, ?)', [skill.name, skill.path, skill.title]);
|
|
983
|
-
}
|
|
984
|
-
for (const route of skillRoutes) {
|
|
985
|
-
const verificationIntents = route.verificationIntents.join(', ');
|
|
986
|
-
database.run('INSERT INTO skill_routes (skill_name, skill_path, trigger, required_input, edit_scope, risk, verification_intents, expected_output) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
987
|
-
route.skillName,
|
|
988
|
-
route.skillPath,
|
|
989
|
-
route.trigger,
|
|
990
|
-
route.requiredInput,
|
|
991
|
-
route.editScope,
|
|
992
|
-
route.risk,
|
|
993
|
-
verificationIntents,
|
|
994
|
-
route.expectedOutput,
|
|
995
|
-
]);
|
|
996
|
-
insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.skillName, 'skill_route_name');
|
|
997
|
-
insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.trigger, 'skill_route_trigger');
|
|
998
|
-
insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.risk, 'skill_route_risk');
|
|
999
|
-
insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.requiredInput, 'skill_route_required_input');
|
|
1000
|
-
insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.editScope, 'skill_route_edit_scope');
|
|
1001
|
-
insertDocumentTerm(database, '.mustflow/skills/INDEX.md', verificationIntents, 'skill_route_verification_intents');
|
|
1002
|
-
}
|
|
1003
|
-
for (const intent of commandIntents) {
|
|
1004
|
-
database.run('INSERT INTO command_intents (name, status, lifecycle, run_policy, description) VALUES (?, ?, ?, ?, ?)', [intent.name, intent.status, intent.lifecycle, intent.runPolicy, intent.description]);
|
|
1005
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.name, 'command_intent');
|
|
1006
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.status, 'command_status');
|
|
1007
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.lifecycle, 'command_lifecycle');
|
|
1008
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.runPolicy, 'command_run_policy');
|
|
1009
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.description, 'command_description');
|
|
1010
|
-
for (const effect of intent.effects) {
|
|
1011
|
-
database.run('INSERT INTO command_effects (intent, source, access, mode, path, lock, concurrency) VALUES (?, ?, ?, ?, ?, ?, ?)', [effect.intent, effect.source, effect.access, effect.mode, effect.path, effect.lock, effect.concurrency]);
|
|
1012
|
-
if (effect.path !== null) {
|
|
1013
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', effect.path, 'command_effect_path');
|
|
1014
|
-
}
|
|
1015
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', effect.lock, 'command_effect_lock');
|
|
1016
|
-
insertDocumentTerm(database, '.mustflow/config/commands.toml', effect.mode, 'command_effect_mode');
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
for (const anchor of sourceAnchors) {
|
|
1020
|
-
database.run('INSERT INTO source_anchors (id, path, line_start, purpose, search_terms, invariant, risk, navigation_only, can_instruct_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1021
|
-
anchor.id,
|
|
1022
|
-
anchor.path,
|
|
1023
|
-
anchor.lineStart,
|
|
1024
|
-
anchor.purpose,
|
|
1025
|
-
anchor.search.join(', '),
|
|
1026
|
-
anchor.invariant,
|
|
1027
|
-
anchor.risk.join(', '),
|
|
1028
|
-
anchor.navigationOnly ? 1 : 0,
|
|
1029
|
-
anchor.canInstructAgent ? 1 : 0,
|
|
1030
|
-
]);
|
|
1031
|
-
database.run('INSERT INTO source_anchor_fingerprints (anchor_id, path, line_start, anchor_metadata_hash, anchor_text_hash, context_hash, search_terms_hash, invariant_hash, risk_hash, symbol_kind, symbol_name, symbol_exported, signature_hash, body_hash, symbol_start_line, symbol_end_line) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1032
|
-
anchor.id,
|
|
1033
|
-
anchor.path,
|
|
1034
|
-
anchor.lineStart,
|
|
1035
|
-
anchor.fingerprint.anchorMetadataHash,
|
|
1036
|
-
anchor.fingerprint.anchorTextHash,
|
|
1037
|
-
anchor.fingerprint.contextHash,
|
|
1038
|
-
anchor.fingerprint.searchTermsHash,
|
|
1039
|
-
anchor.fingerprint.invariantHash,
|
|
1040
|
-
anchor.fingerprint.riskHash,
|
|
1041
|
-
anchor.fingerprint.symbol.kind,
|
|
1042
|
-
anchor.fingerprint.symbol.name,
|
|
1043
|
-
anchor.fingerprint.symbol.exported ? 1 : 0,
|
|
1044
|
-
anchor.fingerprint.symbol.signatureHash,
|
|
1045
|
-
anchor.fingerprint.symbol.bodyHash,
|
|
1046
|
-
anchor.fingerprint.symbol.startLine,
|
|
1047
|
-
anchor.fingerprint.symbol.endLine,
|
|
1048
|
-
]);
|
|
1049
|
-
database.run('INSERT INTO source_anchor_status (anchor_id, status, confidence, identity_signal, location_signal, symbol_signal, body_signal, metadata_signal, semantic_signal, risk_signal, navigation_only, can_instruct_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1050
|
-
anchor.id,
|
|
1051
|
-
anchor.status,
|
|
1052
|
-
anchor.confidence,
|
|
1053
|
-
anchor.signals.identity,
|
|
1054
|
-
anchor.signals.location,
|
|
1055
|
-
anchor.signals.symbol,
|
|
1056
|
-
anchor.signals.body,
|
|
1057
|
-
anchor.signals.metadata,
|
|
1058
|
-
anchor.signals.semantic,
|
|
1059
|
-
anchor.signals.risk,
|
|
1060
|
-
anchor.navigationOnly ? 1 : 0,
|
|
1061
|
-
anchor.canInstructAgent ? 1 : 0,
|
|
1062
|
-
]);
|
|
1063
|
-
}
|
|
1064
|
-
for (const summary of verificationEvidence.summaries) {
|
|
1065
|
-
database.run('INSERT INTO verification_evidence_summaries (source_path, source_hash, command, kind, status, run_dir, manifest_path, verification_plan_id, completion_status, primary_reason, matched_intents, ran_intents, passed_intents, failed_intents, skipped_intents, receipt_count, coverage_count, remaining_risk_count, failure_fingerprint) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1066
|
-
summary.sourcePath,
|
|
1067
|
-
summary.sourceHash,
|
|
1068
|
-
summary.command,
|
|
1069
|
-
summary.kind,
|
|
1070
|
-
summary.status,
|
|
1071
|
-
summary.runDir,
|
|
1072
|
-
summary.manifestPath,
|
|
1073
|
-
summary.verificationPlanId,
|
|
1074
|
-
summary.completionStatus,
|
|
1075
|
-
summary.primaryReason,
|
|
1076
|
-
summary.matchedIntents,
|
|
1077
|
-
summary.ranIntents,
|
|
1078
|
-
summary.passedIntents,
|
|
1079
|
-
summary.failedIntents,
|
|
1080
|
-
summary.skippedIntents,
|
|
1081
|
-
summary.receiptCount,
|
|
1082
|
-
summary.coverageCount,
|
|
1083
|
-
summary.remainingRiskCount,
|
|
1084
|
-
summary.failureFingerprint,
|
|
1085
|
-
]);
|
|
1086
|
-
}
|
|
1087
|
-
for (const plan of verificationEvidence.verificationPlans) {
|
|
1088
|
-
database.run('INSERT INTO verification_plans (plan_id, source_path, classification_hash, command_contract_hash, selected_intents_hash, created_at, source_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1089
|
-
plan.planId,
|
|
1090
|
-
plan.sourcePath,
|
|
1091
|
-
plan.classificationHash,
|
|
1092
|
-
plan.commandContractHash,
|
|
1093
|
-
plan.selectedIntentsHash,
|
|
1094
|
-
plan.createdAt,
|
|
1095
|
-
plan.sourceHash,
|
|
1096
|
-
]);
|
|
1097
|
-
}
|
|
1098
|
-
for (const criterion of verificationEvidence.acceptanceCriteria) {
|
|
1099
|
-
database.run('INSERT INTO acceptance_criteria (criterion_id, plan_id, source, statement_hash, reason, surface, path_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1100
|
-
criterion.criterionId,
|
|
1101
|
-
criterion.planId,
|
|
1102
|
-
criterion.source,
|
|
1103
|
-
criterion.statementHash,
|
|
1104
|
-
criterion.reason,
|
|
1105
|
-
criterion.surface,
|
|
1106
|
-
criterion.pathHash,
|
|
1107
|
-
]);
|
|
1108
|
-
}
|
|
1109
|
-
for (const coverage of verificationEvidence.criterionCoverage) {
|
|
1110
|
-
database.run('INSERT INTO criterion_coverage (criterion_id, plan_id, status, receipt_count, gap_count, risk_count) VALUES (?, ?, ?, ?, ?, ?)', [coverage.criterionId, coverage.planId, coverage.status, coverage.receiptCount, coverage.gapCount, coverage.riskCount]);
|
|
1111
|
-
}
|
|
1112
|
-
for (const receipt of verificationEvidence.receipts) {
|
|
1113
|
-
database.run('INSERT INTO verification_receipt_summaries (source_path, ordinal, intent, status, skipped, verification_plan_id, receipt_path, receipt_sha256) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1114
|
-
receipt.sourcePath,
|
|
1115
|
-
receipt.ordinal,
|
|
1116
|
-
receipt.intent,
|
|
1117
|
-
receipt.status,
|
|
1118
|
-
receipt.skipped ? 1 : 0,
|
|
1119
|
-
receipt.verificationPlanId,
|
|
1120
|
-
receipt.receiptPath,
|
|
1121
|
-
receipt.receiptSha256,
|
|
1122
|
-
]);
|
|
1123
|
-
}
|
|
1124
|
-
for (const receipt of verificationEvidence.commandReceiptSummaries) {
|
|
1125
|
-
database.run('INSERT INTO command_receipt_summaries (receipt_hash, plan_id, intent, status, command_fingerprint, contract_fingerprint, current_state_hash, write_drift_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1126
|
-
receipt.receiptHash,
|
|
1127
|
-
receipt.planId,
|
|
1128
|
-
receipt.intent,
|
|
1129
|
-
receipt.status,
|
|
1130
|
-
receipt.commandFingerprint,
|
|
1131
|
-
receipt.contractFingerprint,
|
|
1132
|
-
receipt.currentStateHash,
|
|
1133
|
-
receipt.writeDriftStatus,
|
|
1134
|
-
]);
|
|
1135
|
-
}
|
|
1136
|
-
for (const coverage of verificationEvidence.coverageStates) {
|
|
1137
|
-
database.run('INSERT INTO verification_coverage_states (source_path, criterion_id, source, status, requirement_reason, intents, receipt_count, gap_count, source_anchor_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1138
|
-
coverage.sourcePath,
|
|
1139
|
-
coverage.criterionId,
|
|
1140
|
-
coverage.source,
|
|
1141
|
-
coverage.status,
|
|
1142
|
-
coverage.requirementReason,
|
|
1143
|
-
joinedList(coverage.intents),
|
|
1144
|
-
coverage.receiptCount,
|
|
1145
|
-
coverage.gapCount,
|
|
1146
|
-
coverage.sourceAnchorCount,
|
|
1147
|
-
]);
|
|
1148
|
-
}
|
|
1149
|
-
for (const risk of verificationEvidence.riskSignals) {
|
|
1150
|
-
database.run('INSERT INTO verification_risk_signals (source_path, ordinal, code, severity, detail_hash) VALUES (?, ?, ?, ?, ?)', [risk.sourcePath, risk.ordinal, risk.code, risk.severity, risk.detailHash]);
|
|
1151
|
-
}
|
|
1152
|
-
for (const signal of verificationEvidence.validationRatchetSignals) {
|
|
1153
|
-
database.run('INSERT INTO validation_ratchet_signals (signal_id, plan_id, code, severity, path_hash, before_hash, after_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [signal.signalId, signal.planId, signal.code, signal.severity, signal.pathHash, signal.beforeHash, signal.afterHash]);
|
|
1154
|
-
}
|
|
1155
|
-
for (const verdict of verificationEvidence.completionVerdictSummaries) {
|
|
1156
|
-
database.run('INSERT INTO completion_verdict_summaries (claim_id, plan_id, status, primary_reason, risk_count, contradiction_count, blocker_count) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1157
|
-
verdict.claimId,
|
|
1158
|
-
verdict.planId,
|
|
1159
|
-
verdict.status,
|
|
1160
|
-
verdict.primaryReason,
|
|
1161
|
-
verdict.riskCount,
|
|
1162
|
-
verdict.contradictionCount,
|
|
1163
|
-
verdict.blockerCount,
|
|
1164
|
-
]);
|
|
1165
|
-
}
|
|
1166
|
-
for (const route of verificationEvidence.reproRoutes) {
|
|
1167
|
-
database.run('INSERT INTO repro_routes (route_id, task_hash, route_digest, route_kind, failure_oracle_hash) VALUES (?, ?, ?, ?, ?)', [route.routeId, route.taskHash, route.routeDigest, route.routeKind, route.failureOracleHash]);
|
|
1168
|
-
}
|
|
1169
|
-
for (const observation of verificationEvidence.reproObservations) {
|
|
1170
|
-
database.run('INSERT INTO repro_observations (route_id, phase, outcome, receipt_hash, diagnostic_fingerprint) VALUES (?, ?, ?, ?, ?)', [
|
|
1171
|
-
observation.routeId,
|
|
1172
|
-
observation.phase,
|
|
1173
|
-
observation.outcome,
|
|
1174
|
-
observation.receiptHash,
|
|
1175
|
-
observation.diagnosticFingerprint,
|
|
1176
|
-
]);
|
|
1177
|
-
}
|
|
1178
|
-
for (const fingerprint of verificationEvidence.failureFingerprintReadModels) {
|
|
1179
|
-
database.run('INSERT INTO failure_fingerprints (fingerprint, plan_id, failed_intents_hash, risk_codes_hash, seen_count, first_seen_at, last_seen_at) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1180
|
-
fingerprint.fingerprint,
|
|
1181
|
-
fingerprint.planId,
|
|
1182
|
-
fingerprint.failedIntentsHash,
|
|
1183
|
-
fingerprint.riskCodesHash,
|
|
1184
|
-
fingerprint.seenCount,
|
|
1185
|
-
fingerprint.firstSeenAt,
|
|
1186
|
-
fingerprint.lastSeenAt,
|
|
1187
|
-
]);
|
|
1188
|
-
}
|
|
1189
|
-
for (const fingerprint of verificationEvidence.failureFingerprints) {
|
|
1190
|
-
database.run('INSERT INTO verification_failure_fingerprints (source_path, fingerprint, verification_plan_id, status, failed_intents, primary_reason, failed_intents_hash, risk_codes_hash, affected_surfaces_hash, first_seen_at, last_seen_at, seen_count, requires_new_evidence) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
|
|
1191
|
-
fingerprint.sourcePath,
|
|
1192
|
-
fingerprint.fingerprint,
|
|
1193
|
-
fingerprint.verificationPlanId,
|
|
1194
|
-
fingerprint.status,
|
|
1195
|
-
joinedList(fingerprint.failedIntents),
|
|
1196
|
-
fingerprint.primaryReason,
|
|
1197
|
-
fingerprint.failedIntentsHash,
|
|
1198
|
-
fingerprint.riskCodesHash,
|
|
1199
|
-
fingerprint.affectedSurfacesHash,
|
|
1200
|
-
fingerprint.firstSeenAt,
|
|
1201
|
-
fingerprint.lastSeenAt,
|
|
1202
|
-
fingerprint.seenCount,
|
|
1203
|
-
fingerprint.requiresNewEvidence ? 1 : 0,
|
|
1204
|
-
]);
|
|
1205
|
-
}
|
|
1206
|
-
for (const signal of sourceAnchorRiskSignals) {
|
|
1207
|
-
database.run('INSERT INTO source_anchor_risk_signals (anchor_id, path_hash, status, risk_signal, confidence, navigation_only, can_instruct_agent) VALUES (?, ?, ?, ?, ?, ?, ?)', [
|
|
1208
|
-
signal.anchorId,
|
|
1209
|
-
signal.pathHash,
|
|
1210
|
-
signal.status,
|
|
1211
|
-
signal.riskSignal,
|
|
1212
|
-
signal.confidence,
|
|
1213
|
-
signal.navigationOnly ? 1 : 0,
|
|
1214
|
-
signal.canInstructAgent ? 1 : 0,
|
|
1215
|
-
]);
|
|
1216
|
-
}
|
|
1217
|
-
populatePathSurfaceReadModel(database);
|
|
1218
|
-
populateSearchTables(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors);
|
|
1219
|
-
database.run('COMMIT');
|
|
1220
|
-
}
|
|
1221
|
-
catch (error) {
|
|
1222
|
-
rollbackTransaction(database);
|
|
1223
|
-
throw error;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
function readCount(database, tableName) {
|
|
1227
|
-
if (!hasTable(database, tableName)) {
|
|
1228
|
-
return 0;
|
|
1229
|
-
}
|
|
1230
|
-
const [row] = queryRows(database, `SELECT COUNT(*) AS count FROM ${tableName}`);
|
|
1231
|
-
const count = row?.count;
|
|
1232
|
-
return typeof count === 'number' && Number.isFinite(count) ? count : 0;
|
|
1233
|
-
}
|
|
1234
|
-
function readStoredIndexedPaths(database) {
|
|
1235
|
-
if (!hasTable(database, 'documents')) {
|
|
1236
|
-
return [];
|
|
1237
|
-
}
|
|
1238
|
-
return queryRows(database, 'SELECT path FROM documents ORDER BY path')
|
|
1239
|
-
.map((row) => toSearchString(row.path))
|
|
1240
|
-
.filter(Boolean);
|
|
1241
|
-
}
|
|
1242
98
|
function createStoredLocalIndexResult(projectRoot, databasePath, dryRun, indexMode, database, capabilities) {
|
|
1243
99
|
return {
|
|
1244
100
|
schema_version: LOCAL_INDEX_SCHEMA_VERSION,
|
|
@@ -1501,159 +357,6 @@ export async function createLocalIndex(projectRoot, options = {}) {
|
|
|
1501
357
|
indexed_paths: indexedFiles.map((file) => file.path),
|
|
1502
358
|
};
|
|
1503
359
|
}
|
|
1504
|
-
function readStoredSchemaVersion(database) {
|
|
1505
|
-
return readMetadataValue(database, 'schema_version');
|
|
1506
|
-
}
|
|
1507
|
-
function getStalePaths(projectRoot, database) {
|
|
1508
|
-
const schemaVersion = readStoredSchemaVersion(database);
|
|
1509
|
-
if (schemaVersion !== LOCAL_INDEX_SCHEMA_VERSION) {
|
|
1510
|
-
return ['.mustflow/cache/mustflow.sqlite'];
|
|
1511
|
-
}
|
|
1512
|
-
if (hasTable(database, 'indexed_files')) {
|
|
1513
|
-
const stalePaths = new Set();
|
|
1514
|
-
const indexedRows = queryRows(database, 'SELECT path, source_scope, content_hash FROM indexed_files');
|
|
1515
|
-
const indexedPaths = new Set(indexedRows.map((row) => toSearchString(row.path)));
|
|
1516
|
-
for (const row of indexedRows) {
|
|
1517
|
-
const indexedPath = toSearchString(row.path);
|
|
1518
|
-
const sourceScope = normalizeIndexedFileSourceScope(toSearchString(row.source_scope));
|
|
1519
|
-
try {
|
|
1520
|
-
const current = readIndexedFileRecord(projectRoot, indexedPath, sourceScope);
|
|
1521
|
-
if (current.contentHash !== toSearchString(row.content_hash)) {
|
|
1522
|
-
stalePaths.add(indexedPath);
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
catch {
|
|
1526
|
-
stalePaths.add(indexedPath);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
for (const document of collectDocuments(projectRoot)) {
|
|
1530
|
-
if (!indexedPaths.has(document.path)) {
|
|
1531
|
-
stalePaths.add(document.path);
|
|
1532
|
-
}
|
|
1533
|
-
}
|
|
1534
|
-
return Array.from(stalePaths).sort((left, right) => left.localeCompare(right));
|
|
1535
|
-
}
|
|
1536
|
-
const indexedRows = queryRows(database, 'SELECT path, content_hash FROM documents');
|
|
1537
|
-
const indexedHashes = new Map(indexedRows.map((row) => [toSearchString(row.path), toSearchString(row.content_hash)]));
|
|
1538
|
-
const currentDocuments = collectDocuments(projectRoot);
|
|
1539
|
-
const currentHashes = new Map(currentDocuments.map((document) => [document.path, document.contentHash]));
|
|
1540
|
-
const stalePaths = new Set();
|
|
1541
|
-
for (const [indexedPath, indexedHash] of indexedHashes) {
|
|
1542
|
-
if (currentHashes.get(indexedPath) !== indexedHash) {
|
|
1543
|
-
stalePaths.add(indexedPath);
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
for (const currentPath of currentHashes.keys()) {
|
|
1547
|
-
if (!indexedHashes.has(currentPath)) {
|
|
1548
|
-
stalePaths.add(currentPath);
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
return Array.from(stalePaths).sort((left, right) => left.localeCompare(right));
|
|
1552
|
-
}
|
|
1553
|
-
function mapCommandLockConflict(row, intent) {
|
|
1554
|
-
const targetIsLeft = toSearchString(row.left_intent) === intent;
|
|
1555
|
-
const targetPrefix = targetIsLeft ? 'left' : 'right';
|
|
1556
|
-
const otherPrefix = targetIsLeft ? 'right' : 'left';
|
|
1557
|
-
return {
|
|
1558
|
-
intent: toSearchString(row[`${otherPrefix}_intent`]),
|
|
1559
|
-
lock: toSearchString(row.lock),
|
|
1560
|
-
paths: splitIndexedList(row[`${targetPrefix}_paths`]),
|
|
1561
|
-
modes: splitIndexedList(row[`${targetPrefix}_modes`]),
|
|
1562
|
-
concurrencies: splitIndexedList(row[`${targetPrefix}_concurrencies`]),
|
|
1563
|
-
conflictingPaths: splitIndexedList(row[`${otherPrefix}_paths`]),
|
|
1564
|
-
conflictingModes: splitIndexedList(row[`${otherPrefix}_modes`]),
|
|
1565
|
-
conflictingConcurrencies: splitIndexedList(row[`${otherPrefix}_concurrencies`]),
|
|
1566
|
-
};
|
|
1567
|
-
}
|
|
1568
|
-
/**
|
|
1569
|
-
* mf:anchor cli.index.command-effect-graph
|
|
1570
|
-
* purpose: Read command-effect lock and conflict explanations from the local SQLite index.
|
|
1571
|
-
* search: mf explain command, command locks, local index, sqlite graph
|
|
1572
|
-
* invariant: Indexed command-effect rows explain current commands.toml only when the index is fresh and never grant command authority.
|
|
1573
|
-
* risk: cache, config
|
|
1574
|
-
*/
|
|
1575
|
-
function queryLocalCommandEffectGraph(databasePath, database, intent) {
|
|
1576
|
-
const writeLocks = queryRows(database, `
|
|
1577
|
-
SELECT lock, paths, modes, sources, concurrencies, effect_count
|
|
1578
|
-
FROM command_write_locks
|
|
1579
|
-
WHERE intent = ?
|
|
1580
|
-
ORDER BY lock
|
|
1581
|
-
`, [intent]).map((row) => ({
|
|
1582
|
-
lock: toSearchString(row.lock),
|
|
1583
|
-
paths: splitIndexedList(row.paths),
|
|
1584
|
-
modes: splitIndexedList(row.modes),
|
|
1585
|
-
sources: splitIndexedList(row.sources),
|
|
1586
|
-
concurrencies: splitIndexedList(row.concurrencies),
|
|
1587
|
-
effectCount: typeof row.effect_count === 'number' && Number.isFinite(row.effect_count) ? row.effect_count : 0,
|
|
1588
|
-
}));
|
|
1589
|
-
const lockConflicts = queryRows(database, `
|
|
1590
|
-
SELECT
|
|
1591
|
-
left_intent,
|
|
1592
|
-
right_intent,
|
|
1593
|
-
lock,
|
|
1594
|
-
left_paths,
|
|
1595
|
-
right_paths,
|
|
1596
|
-
left_modes,
|
|
1597
|
-
right_modes,
|
|
1598
|
-
left_concurrencies,
|
|
1599
|
-
right_concurrencies
|
|
1600
|
-
FROM command_lock_conflicts
|
|
1601
|
-
WHERE left_intent = ? OR right_intent = ?
|
|
1602
|
-
ORDER BY lock, left_intent, right_intent
|
|
1603
|
-
`, [intent, intent]).map((row) => mapCommandLockConflict(row, intent));
|
|
1604
|
-
return {
|
|
1605
|
-
source: 'local_index',
|
|
1606
|
-
authority: 'explanation_only',
|
|
1607
|
-
commandAuthority: '.mustflow/config/commands.toml',
|
|
1608
|
-
grantsCommandAuthority: false,
|
|
1609
|
-
status: 'fresh',
|
|
1610
|
-
databasePath,
|
|
1611
|
-
indexFresh: true,
|
|
1612
|
-
stalePaths: [],
|
|
1613
|
-
writeLocks,
|
|
1614
|
-
lockConflicts,
|
|
1615
|
-
refreshHint: null,
|
|
1616
|
-
};
|
|
1617
|
-
}
|
|
1618
|
-
export async function readLocalCommandEffectGraph(projectRoot, intent) {
|
|
1619
|
-
const graphs = await readLocalCommandEffectGraphs(projectRoot, [intent]);
|
|
1620
|
-
return graphs.get(intent) ?? createCommandEffectGraphStatus(getLocalIndexDatabasePath(projectRoot), 'unreadable');
|
|
1621
|
-
}
|
|
1622
|
-
export async function readLocalCommandEffectGraphs(projectRoot, intents) {
|
|
1623
|
-
const databasePath = getLocalIndexDatabasePath(projectRoot);
|
|
1624
|
-
const intentNames = [...new Set(intents)];
|
|
1625
|
-
const statusMap = (status, stalePaths = []) => new Map(intentNames.map((intent) => [intent, createCommandEffectGraphStatus(databasePath, status, stalePaths)]));
|
|
1626
|
-
if (!existsSync(databasePath)) {
|
|
1627
|
-
return statusMap('missing');
|
|
1628
|
-
}
|
|
1629
|
-
const SQL = await loadSqlJs();
|
|
1630
|
-
const database = new SQL.Database(readFileSync(databasePath));
|
|
1631
|
-
try {
|
|
1632
|
-
const stalePaths = getStalePaths(projectRoot, database);
|
|
1633
|
-
if (stalePaths.length > 0) {
|
|
1634
|
-
return statusMap('stale', stalePaths);
|
|
1635
|
-
}
|
|
1636
|
-
return new Map(intentNames.map((intent) => [intent, queryLocalCommandEffectGraph(databasePath, database, intent)]));
|
|
1637
|
-
}
|
|
1638
|
-
catch {
|
|
1639
|
-
return statusMap('unreadable');
|
|
1640
|
-
}
|
|
1641
|
-
finally {
|
|
1642
|
-
database.close();
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
function createPathSurfaceReadModelStatus(databasePath, status, inputPath, stalePaths = []) {
|
|
1646
|
-
return {
|
|
1647
|
-
source: 'local_index',
|
|
1648
|
-
status,
|
|
1649
|
-
databasePath,
|
|
1650
|
-
indexFresh: status === 'fresh',
|
|
1651
|
-
stalePaths,
|
|
1652
|
-
inputPath,
|
|
1653
|
-
match: null,
|
|
1654
|
-
refreshHint: status === 'fresh' ? null : 'Run `mf index` to refresh path-surface explanations.',
|
|
1655
|
-
};
|
|
1656
|
-
}
|
|
1657
360
|
function createLocalIndexPromptContextStatus(databasePath, status, stalePaths = [], capabilities = null) {
|
|
1658
361
|
return {
|
|
1659
362
|
source: 'local_index',
|
|
@@ -1903,122 +606,6 @@ export async function readLocalVerificationReadModelQueriesForPlan(projectRoot,
|
|
|
1903
606
|
export async function readLatestLocalVerificationReadModelQueries(projectRoot) {
|
|
1904
607
|
return readLocalVerificationReadModelQueriesForPlan(projectRoot, null);
|
|
1905
608
|
}
|
|
1906
|
-
function pathSurfaceReadModelWithMatch(base, match) {
|
|
1907
|
-
return {
|
|
1908
|
-
...base,
|
|
1909
|
-
match,
|
|
1910
|
-
};
|
|
1911
|
-
}
|
|
1912
|
-
function readPathSurfaceReasonMap(database) {
|
|
1913
|
-
const byRule = new Map();
|
|
1914
|
-
for (const row of queryRows(database, 'SELECT rule_id, reason_kind, reason FROM path_surface_reasons ORDER BY rule_id, reason_kind, ordinal')) {
|
|
1915
|
-
const ruleId = toSearchString(row.rule_id);
|
|
1916
|
-
const reasonKind = toSearchString(row.reason_kind);
|
|
1917
|
-
const reason = toSearchString(row.reason);
|
|
1918
|
-
let reasonsByKind = byRule.get(ruleId);
|
|
1919
|
-
if (!reasonsByKind) {
|
|
1920
|
-
reasonsByKind = new Map();
|
|
1921
|
-
byRule.set(ruleId, reasonsByKind);
|
|
1922
|
-
}
|
|
1923
|
-
const reasons = reasonsByKind.get(reasonKind) ?? [];
|
|
1924
|
-
reasons.push(reason);
|
|
1925
|
-
reasonsByKind.set(reasonKind, reasons);
|
|
1926
|
-
}
|
|
1927
|
-
return byRule;
|
|
1928
|
-
}
|
|
1929
|
-
function readPathSurfaceRuleMatches(database) {
|
|
1930
|
-
const reasons = readPathSurfaceReasonMap(database);
|
|
1931
|
-
return queryRows(database, 'SELECT rule_id, pattern_kind, pattern, pattern_flags, surface_kind, category, is_public_surface, update_policy FROM path_surfaces ORDER BY rowid').map((row) => {
|
|
1932
|
-
const ruleId = toSearchString(row.rule_id);
|
|
1933
|
-
const reasonsByKind = reasons.get(ruleId);
|
|
1934
|
-
const reasonList = (kind) => reasonsByKind?.get(kind) ?? [];
|
|
1935
|
-
return {
|
|
1936
|
-
ruleId,
|
|
1937
|
-
patternKind: toSearchString(row.pattern_kind),
|
|
1938
|
-
pattern: toSearchString(row.pattern),
|
|
1939
|
-
patternFlags: toSearchString(row.pattern_flags),
|
|
1940
|
-
changeKinds: reasonList('change_kind'),
|
|
1941
|
-
surface: {
|
|
1942
|
-
kind: toSearchString(row.surface_kind),
|
|
1943
|
-
category: toSearchString(row.category),
|
|
1944
|
-
isPublicSurface: Number(row.is_public_surface) === 1,
|
|
1945
|
-
validationReasons: reasonList('validation_reason'),
|
|
1946
|
-
affectedContracts: reasonList('affected_contract'),
|
|
1947
|
-
updatePolicy: toSearchString(row.update_policy),
|
|
1948
|
-
driftChecks: reasonList('drift_check'),
|
|
1949
|
-
},
|
|
1950
|
-
};
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
function matchPathSurfaceRule(relativePath, rules) {
|
|
1954
|
-
if (!relativePath) {
|
|
1955
|
-
return null;
|
|
1956
|
-
}
|
|
1957
|
-
for (const rule of rules) {
|
|
1958
|
-
try {
|
|
1959
|
-
if (new RegExp(rule.pattern, rule.patternFlags).test(relativePath)) {
|
|
1960
|
-
return rule;
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
catch {
|
|
1964
|
-
continue;
|
|
1965
|
-
}
|
|
1966
|
-
}
|
|
1967
|
-
return null;
|
|
1968
|
-
}
|
|
1969
|
-
export async function readLocalPathSurfaces(projectRoot, relativePaths) {
|
|
1970
|
-
const databasePath = getLocalIndexDatabasePath(projectRoot);
|
|
1971
|
-
const normalizedPaths = [...new Set(relativePaths.map((relativePath) => toPosixPath(relativePath)).filter(Boolean))];
|
|
1972
|
-
const statusMap = (status, stalePaths = []) => new Map(normalizedPaths.map((relativePath) => [
|
|
1973
|
-
relativePath,
|
|
1974
|
-
createPathSurfaceReadModelStatus(databasePath, status, relativePath, stalePaths),
|
|
1975
|
-
]));
|
|
1976
|
-
if (!existsSync(databasePath)) {
|
|
1977
|
-
return statusMap('missing');
|
|
1978
|
-
}
|
|
1979
|
-
const SQL = await loadSqlJs();
|
|
1980
|
-
const database = new SQL.Database(readFileSync(databasePath));
|
|
1981
|
-
try {
|
|
1982
|
-
const stalePaths = getStalePaths(projectRoot, database);
|
|
1983
|
-
if (stalePaths.length > 0) {
|
|
1984
|
-
return statusMap('stale', stalePaths);
|
|
1985
|
-
}
|
|
1986
|
-
const rules = readPathSurfaceRuleMatches(database);
|
|
1987
|
-
return new Map(normalizedPaths.map((relativePath) => {
|
|
1988
|
-
const base = createPathSurfaceReadModelStatus(databasePath, 'fresh', relativePath);
|
|
1989
|
-
return [relativePath, pathSurfaceReadModelWithMatch(base, matchPathSurfaceRule(relativePath, rules))];
|
|
1990
|
-
}));
|
|
1991
|
-
}
|
|
1992
|
-
catch {
|
|
1993
|
-
return statusMap('unreadable');
|
|
1994
|
-
}
|
|
1995
|
-
finally {
|
|
1996
|
-
database.close();
|
|
1997
|
-
}
|
|
1998
|
-
}
|
|
1999
|
-
export async function readLocalPathSurface(projectRoot, relativePath) {
|
|
2000
|
-
const databasePath = getLocalIndexDatabasePath(projectRoot);
|
|
2001
|
-
const inputPath = relativePath ? toPosixPath(relativePath) : null;
|
|
2002
|
-
if (!inputPath) {
|
|
2003
|
-
if (!existsSync(databasePath)) {
|
|
2004
|
-
return createPathSurfaceReadModelStatus(databasePath, 'missing', null);
|
|
2005
|
-
}
|
|
2006
|
-
const SQL = await loadSqlJs();
|
|
2007
|
-
const database = new SQL.Database(readFileSync(databasePath));
|
|
2008
|
-
try {
|
|
2009
|
-
const stalePaths = getStalePaths(projectRoot, database);
|
|
2010
|
-
return createPathSurfaceReadModelStatus(databasePath, stalePaths.length > 0 ? 'stale' : 'fresh', null, stalePaths);
|
|
2011
|
-
}
|
|
2012
|
-
catch {
|
|
2013
|
-
return createPathSurfaceReadModelStatus(databasePath, 'unreadable', null);
|
|
2014
|
-
}
|
|
2015
|
-
finally {
|
|
2016
|
-
database.close();
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
const surfaces = await readLocalPathSurfaces(projectRoot, [inputPath]);
|
|
2020
|
-
return surfaces.get(inputPath) ?? createPathSurfaceReadModelStatus(databasePath, 'unreadable', inputPath);
|
|
2021
|
-
}
|
|
2022
609
|
export async function readLocalSourceAnchorVerdictRisks(projectRoot, relativePaths) {
|
|
2023
610
|
const databasePath = getLocalIndexDatabasePath(projectRoot);
|
|
2024
611
|
const normalizedPaths = new Set(relativePaths.map((relativePath) => toPosixPath(relativePath)).filter(Boolean));
|
|
@@ -2075,444 +662,3 @@ ORDER BY source_anchors.id
|
|
|
2075
662
|
database.close();
|
|
2076
663
|
}
|
|
2077
664
|
}
|
|
2078
|
-
function getSectionHeadings(database, documentPath) {
|
|
2079
|
-
return queryRows(database, 'SELECT heading FROM sections WHERE document_path = ? ORDER BY ordinal', [documentPath]).map((row) => toSearchString(row.heading));
|
|
2080
|
-
}
|
|
2081
|
-
function getDocumentTerms(database, documentPath) {
|
|
2082
|
-
return queryRows(database, 'SELECT term FROM document_terms WHERE document_path = ? ORDER BY term', [documentPath]).map((row) => toSearchString(row.term));
|
|
2083
|
-
}
|
|
2084
|
-
function commandEffectFromRow(row) {
|
|
2085
|
-
return {
|
|
2086
|
-
intent: toSearchString(row.intent),
|
|
2087
|
-
source: toSearchString(row.source),
|
|
2088
|
-
access: toSearchString(row.access),
|
|
2089
|
-
mode: toSearchString(row.mode),
|
|
2090
|
-
path: row.path === null || row.path === undefined ? null : toSearchString(row.path),
|
|
2091
|
-
lock: toSearchString(row.lock),
|
|
2092
|
-
concurrency: toSearchString(row.concurrency),
|
|
2093
|
-
};
|
|
2094
|
-
}
|
|
2095
|
-
function sqlPlaceholders(values) {
|
|
2096
|
-
return values.map(() => '?').join(', ');
|
|
2097
|
-
}
|
|
2098
|
-
function queryCandidateRows(database, sql, keyColumn, candidates, indexedMatches) {
|
|
2099
|
-
if (!indexedMatches.active || candidates.size === 0) {
|
|
2100
|
-
return queryRows(database, sql);
|
|
2101
|
-
}
|
|
2102
|
-
const keys = [...candidates].sort((left, right) => left.localeCompare(right));
|
|
2103
|
-
return queryRows(database, `${sql} WHERE ${keyColumn} IN (${sqlPlaceholders(keys)})`, keys);
|
|
2104
|
-
}
|
|
2105
|
-
function getCommandEffectsByIntent(database, intents) {
|
|
2106
|
-
const uniqueIntents = [...new Set(intents)].sort((left, right) => left.localeCompare(right));
|
|
2107
|
-
const effectsByIntent = new Map(uniqueIntents.map((intent) => [intent, []]));
|
|
2108
|
-
if (uniqueIntents.length === 0) {
|
|
2109
|
-
return effectsByIntent;
|
|
2110
|
-
}
|
|
2111
|
-
for (const row of queryRows(database, `SELECT intent, source, access, mode, path, lock, concurrency
|
|
2112
|
-
FROM command_effects
|
|
2113
|
-
WHERE intent IN (${sqlPlaceholders(uniqueIntents)})
|
|
2114
|
-
ORDER BY intent, lock, path, mode`, uniqueIntents)) {
|
|
2115
|
-
const effect = commandEffectFromRow(row);
|
|
2116
|
-
const effects = effectsByIntent.get(effect.intent);
|
|
2117
|
-
if (effects) {
|
|
2118
|
-
effects.push(effect);
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
return effectsByIntent;
|
|
2122
|
-
}
|
|
2123
|
-
const EMPTY_INDEXED_SEARCH_MATCHES = {
|
|
2124
|
-
active: false,
|
|
2125
|
-
documents: new Set(),
|
|
2126
|
-
skills: new Set(),
|
|
2127
|
-
skillRoutes: new Set(),
|
|
2128
|
-
commandIntents: new Set(),
|
|
2129
|
-
sourceAnchors: new Set(),
|
|
2130
|
-
};
|
|
2131
|
-
function buildFtsQuery(query) {
|
|
2132
|
-
const tokens = extractSearchTokens(query);
|
|
2133
|
-
if (tokens.length === 0) {
|
|
2134
|
-
return null;
|
|
2135
|
-
}
|
|
2136
|
-
return [...new Set(tokens)].map((token) => `"${token.replaceAll('"', '""')}"`).join(' AND ');
|
|
2137
|
-
}
|
|
2138
|
-
function queryFtsSet(database, sql, ftsQuery, column) {
|
|
2139
|
-
return new Set(queryRows(database, sql, [ftsQuery]).map((row) => toSearchString(row[column])));
|
|
2140
|
-
}
|
|
2141
|
-
function mergeSearchSets(left, right) {
|
|
2142
|
-
return new Set([...left, ...right]);
|
|
2143
|
-
}
|
|
2144
|
-
function mergeIndexedSearchMatches(left, right) {
|
|
2145
|
-
return {
|
|
2146
|
-
active: left.active || right.active,
|
|
2147
|
-
documents: mergeSearchSets(left.documents, right.documents),
|
|
2148
|
-
skills: mergeSearchSets(left.skills, right.skills),
|
|
2149
|
-
skillRoutes: mergeSearchSets(left.skillRoutes, right.skillRoutes),
|
|
2150
|
-
commandIntents: mergeSearchSets(left.commandIntents, right.commandIntents),
|
|
2151
|
-
sourceAnchors: mergeSearchSets(left.sourceAnchors, right.sourceAnchors),
|
|
2152
|
-
};
|
|
2153
|
-
}
|
|
2154
|
-
function queryNgramSet(database, targetKind, grams) {
|
|
2155
|
-
const placeholders = grams.map(() => '?').join(', ');
|
|
2156
|
-
if (!placeholders) {
|
|
2157
|
-
return new Set();
|
|
2158
|
-
}
|
|
2159
|
-
return new Set(queryRows(database, `SELECT target_key
|
|
2160
|
-
FROM search_ngrams
|
|
2161
|
-
WHERE target_kind = ? AND gram IN (${placeholders})
|
|
2162
|
-
GROUP BY target_key
|
|
2163
|
-
HAVING COUNT(DISTINCT gram) = ?`, [targetKind, ...grams, grams.length]).map((row) => toSearchString(row.target_key)));
|
|
2164
|
-
}
|
|
2165
|
-
function getNgramSearchMatches(database, query) {
|
|
2166
|
-
if (!hasTable(database, 'search_ngrams')) {
|
|
2167
|
-
return EMPTY_INDEXED_SEARCH_MATCHES;
|
|
2168
|
-
}
|
|
2169
|
-
const grams = buildSearchNgrams([query]);
|
|
2170
|
-
if (grams.length === 0) {
|
|
2171
|
-
return EMPTY_INDEXED_SEARCH_MATCHES;
|
|
2172
|
-
}
|
|
2173
|
-
return {
|
|
2174
|
-
active: true,
|
|
2175
|
-
documents: queryNgramSet(database, 'document', grams),
|
|
2176
|
-
skills: queryNgramSet(database, 'skill', grams),
|
|
2177
|
-
skillRoutes: queryNgramSet(database, 'skill_route', grams),
|
|
2178
|
-
commandIntents: queryNgramSet(database, 'command_intent', grams),
|
|
2179
|
-
sourceAnchors: queryNgramSet(database, 'source_anchor', grams),
|
|
2180
|
-
};
|
|
2181
|
-
}
|
|
2182
|
-
function getIndexedSearchMatches(database, query) {
|
|
2183
|
-
const capabilities = readStoredSearchCapabilities(database);
|
|
2184
|
-
const ftsQuery = capabilities.backend === SEARCH_BACKEND_FTS5 ? buildFtsQuery(query) : null;
|
|
2185
|
-
const ngramMatches = getNgramSearchMatches(database, query);
|
|
2186
|
-
if (!ftsQuery) {
|
|
2187
|
-
return ngramMatches;
|
|
2188
|
-
}
|
|
2189
|
-
try {
|
|
2190
|
-
const ftsMatches = {
|
|
2191
|
-
active: true,
|
|
2192
|
-
documents: queryFtsSet(database, 'SELECT path FROM search_documents_fts WHERE search_documents_fts MATCH ?', ftsQuery, 'path'),
|
|
2193
|
-
skills: queryFtsSet(database, 'SELECT name FROM search_skills_fts WHERE search_skills_fts MATCH ?', ftsQuery, 'name'),
|
|
2194
|
-
skillRoutes: queryFtsSet(database, 'SELECT route_key FROM search_skill_routes_fts WHERE search_skill_routes_fts MATCH ?', ftsQuery, 'route_key'),
|
|
2195
|
-
commandIntents: queryFtsSet(database, 'SELECT name FROM search_command_intents_fts WHERE search_command_intents_fts MATCH ?', ftsQuery, 'name'),
|
|
2196
|
-
sourceAnchors: queryFtsSet(database, 'SELECT id FROM search_source_anchors_fts WHERE search_source_anchors_fts MATCH ?', ftsQuery, 'id'),
|
|
2197
|
-
};
|
|
2198
|
-
return mergeIndexedSearchMatches(ftsMatches, ngramMatches);
|
|
2199
|
-
}
|
|
2200
|
-
catch {
|
|
2201
|
-
return ngramMatches;
|
|
2202
|
-
}
|
|
2203
|
-
}
|
|
2204
|
-
function matchesIndexedOrTableScan(fields, query, indexedMatches, matchSet, key) {
|
|
2205
|
-
return indexedMatches.active && matchSet.size > 0 ? matchSet.has(key) : isMatched(fields, query);
|
|
2206
|
-
}
|
|
2207
|
-
function scoreIndexedOrTableScan(primaryFields, secondaryFields, query, indexedMatches, matchSet, key) {
|
|
2208
|
-
const tableScore = scoreMatch(primaryFields, secondaryFields, query);
|
|
2209
|
-
return indexedMatches.active && matchSet.size > 0 && matchSet.has(key) ? Math.max(tableScore, 20) : tableScore;
|
|
2210
|
-
}
|
|
2211
|
-
function sortLocalSearchResults(results, scope, limit) {
|
|
2212
|
-
const sorted = [...results].sort((left, right) => {
|
|
2213
|
-
if (scope === 'all' && left.authority_rank !== right.authority_rank) {
|
|
2214
|
-
return left.authority_rank - right.authority_rank;
|
|
2215
|
-
}
|
|
2216
|
-
return right.score - left.score || (left.path ?? left.name ?? '').localeCompare(right.path ?? right.name ?? '');
|
|
2217
|
-
});
|
|
2218
|
-
const limited = sorted.slice(0, limit);
|
|
2219
|
-
if (scope !== 'all' || limited.some((item) => item.kind === 'source_anchor')) {
|
|
2220
|
-
return limited;
|
|
2221
|
-
}
|
|
2222
|
-
const sourceAnchor = sorted.find((item) => item.kind === 'source_anchor');
|
|
2223
|
-
if (!sourceAnchor) {
|
|
2224
|
-
return limited;
|
|
2225
|
-
}
|
|
2226
|
-
if (limited.length < limit) {
|
|
2227
|
-
return [...limited, sourceAnchor];
|
|
2228
|
-
}
|
|
2229
|
-
return [...limited.slice(0, Math.max(0, limit - 1)), sourceAnchor];
|
|
2230
|
-
}
|
|
2231
|
-
function collectBoundedDirectSearchDocuments(projectRoot) {
|
|
2232
|
-
const documents = [];
|
|
2233
|
-
const relativePaths = getExistingIndexablePaths(projectRoot).slice(0, DIRECT_SEARCH_MAX_WORKFLOW_FILES);
|
|
2234
|
-
for (const relativePath of relativePaths) {
|
|
2235
|
-
try {
|
|
2236
|
-
documents.push(...collectDocumentsFromPaths(projectRoot, [relativePath]));
|
|
2237
|
-
}
|
|
2238
|
-
catch {
|
|
2239
|
-
continue;
|
|
2240
|
-
}
|
|
2241
|
-
}
|
|
2242
|
-
return documents;
|
|
2243
|
-
}
|
|
2244
|
-
function searchLocalWorkflowFilesDirectly(projectRoot, databasePath, normalizedQuery, limit, scope) {
|
|
2245
|
-
const cacheLayers = readCacheLayerSets(projectRoot);
|
|
2246
|
-
const results = [];
|
|
2247
|
-
if (scope === 'workflow' || scope === 'all') {
|
|
2248
|
-
const documents = collectBoundedDirectSearchDocuments(projectRoot);
|
|
2249
|
-
for (const document of documents) {
|
|
2250
|
-
let searchableContent = document.contentSnippet;
|
|
2251
|
-
try {
|
|
2252
|
-
searchableContent = readText(projectRoot, document.path);
|
|
2253
|
-
}
|
|
2254
|
-
catch {
|
|
2255
|
-
searchableContent = document.contentSnippet;
|
|
2256
|
-
}
|
|
2257
|
-
const primaryFields = [document.path, document.title];
|
|
2258
|
-
const secondaryFields = [document.type, searchableContent, ...document.sections];
|
|
2259
|
-
const fields = [...primaryFields, ...secondaryFields];
|
|
2260
|
-
if (!isMatched(fields, normalizedQuery)) {
|
|
2261
|
-
continue;
|
|
2262
|
-
}
|
|
2263
|
-
results.push(withCacheHint({
|
|
2264
|
-
kind: 'document',
|
|
2265
|
-
path: document.path,
|
|
2266
|
-
title: document.title,
|
|
2267
|
-
document_type: document.type,
|
|
2268
|
-
...workflowAuthorityForDocument(document.type),
|
|
2269
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2270
|
-
score: scoreMatch(primaryFields, secondaryFields, normalizedQuery),
|
|
2271
|
-
}, cacheLayers));
|
|
2272
|
-
}
|
|
2273
|
-
for (const skill of collectSkills(documents)) {
|
|
2274
|
-
const fields = [skill.name, skill.path, skill.title];
|
|
2275
|
-
if (!isMatched(fields, normalizedQuery)) {
|
|
2276
|
-
continue;
|
|
2277
|
-
}
|
|
2278
|
-
results.push(withCacheHint({
|
|
2279
|
-
kind: 'skill',
|
|
2280
|
-
name: skill.name,
|
|
2281
|
-
path: skill.path,
|
|
2282
|
-
title: skill.title,
|
|
2283
|
-
...skillAuthority(),
|
|
2284
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2285
|
-
score: scoreMatch(fields, [], normalizedQuery),
|
|
2286
|
-
}, cacheLayers));
|
|
2287
|
-
}
|
|
2288
|
-
}
|
|
2289
|
-
const sortedResults = sortLocalSearchResults(results, scope, limit);
|
|
2290
|
-
return {
|
|
2291
|
-
schema_version: LOCAL_INDEX_SCHEMA_VERSION,
|
|
2292
|
-
command: 'search',
|
|
2293
|
-
ok: true,
|
|
2294
|
-
mustflow_root: path.resolve(projectRoot),
|
|
2295
|
-
database_path: databasePath,
|
|
2296
|
-
query: normalizedQuery,
|
|
2297
|
-
limit,
|
|
2298
|
-
scope,
|
|
2299
|
-
index_fresh: false,
|
|
2300
|
-
stale_paths: [],
|
|
2301
|
-
search_backend: SEARCH_BACKEND_TABLE_SCAN,
|
|
2302
|
-
search_fts5_available: false,
|
|
2303
|
-
result_count: sortedResults.length,
|
|
2304
|
-
results: sortedResults,
|
|
2305
|
-
};
|
|
2306
|
-
}
|
|
2307
|
-
function isLocalIndexStaleError(error) {
|
|
2308
|
-
return error instanceof Error && error.message.startsWith('Local mustflow index is stale:');
|
|
2309
|
-
}
|
|
2310
|
-
function isLocalIndexRuntimeUnavailableError(error) {
|
|
2311
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2312
|
-
return /file is not a database|database disk image is malformed|no such table|no such column|sqlite|sql\.js/iu.test(message);
|
|
2313
|
-
}
|
|
2314
|
-
/**
|
|
2315
|
-
* mf:anchor cli.search.local-index
|
|
2316
|
-
* purpose: Search the local index while preserving workflow authority above source navigation hints.
|
|
2317
|
-
* search: mf search, scope workflow source all, authority rank, navigation only
|
|
2318
|
-
* invariant: Source anchor results remain navigation-only and cannot outrank command or workflow authority.
|
|
2319
|
-
* risk: cache, config
|
|
2320
|
-
*/
|
|
2321
|
-
export async function searchLocalIndex(projectRoot, query, options = {}) {
|
|
2322
|
-
const normalizedQuery = normalizeSearchText(query);
|
|
2323
|
-
const limit = Math.max(1, Math.min(options.limit ?? 10, 50));
|
|
2324
|
-
const scope = options.scope ?? 'workflow';
|
|
2325
|
-
const databasePath = getLocalIndexDatabasePath(projectRoot);
|
|
2326
|
-
if (normalizedQuery.length === 0) {
|
|
2327
|
-
throw new Error('Search query must not be empty.');
|
|
2328
|
-
}
|
|
2329
|
-
if (!existsSync(databasePath)) {
|
|
2330
|
-
return searchLocalWorkflowFilesDirectly(projectRoot, databasePath, normalizedQuery, limit, scope);
|
|
2331
|
-
}
|
|
2332
|
-
let database;
|
|
2333
|
-
try {
|
|
2334
|
-
const SQL = await loadSqlJs();
|
|
2335
|
-
database = new SQL.Database(readFileSync(databasePath));
|
|
2336
|
-
}
|
|
2337
|
-
catch {
|
|
2338
|
-
return searchLocalWorkflowFilesDirectly(projectRoot, databasePath, normalizedQuery, limit, scope);
|
|
2339
|
-
}
|
|
2340
|
-
let capabilities = searchCapabilities(false);
|
|
2341
|
-
const results = [];
|
|
2342
|
-
try {
|
|
2343
|
-
const cacheLayers = readCacheLayerSets(projectRoot);
|
|
2344
|
-
const stalePaths = getStalePaths(projectRoot, database);
|
|
2345
|
-
capabilities = readStoredSearchCapabilities(database);
|
|
2346
|
-
const indexedMatches = getIndexedSearchMatches(database, normalizedQuery);
|
|
2347
|
-
if (stalePaths.length > 0) {
|
|
2348
|
-
throw new Error(`Local mustflow index is stale: ${stalePaths.join(', ')}. Run \`mf index\` before searching. Refresh command: mf index`);
|
|
2349
|
-
}
|
|
2350
|
-
if (scope === 'workflow' || scope === 'all') {
|
|
2351
|
-
for (const row of queryCandidateRows(database, 'SELECT path, type, title, content_snippet FROM documents', 'path', indexedMatches.documents, indexedMatches)) {
|
|
2352
|
-
const pathValue = toSearchString(row.path);
|
|
2353
|
-
const typeValue = toSearchString(row.type);
|
|
2354
|
-
const title = toSearchString(row.title);
|
|
2355
|
-
const contentSnippet = toSearchString(row.content_snippet);
|
|
2356
|
-
const sectionHeadings = getSectionHeadings(database, pathValue);
|
|
2357
|
-
const documentTerms = getDocumentTerms(database, pathValue);
|
|
2358
|
-
const primaryFields = [pathValue, title];
|
|
2359
|
-
const secondaryFields = [typeValue, contentSnippet, ...sectionHeadings, ...documentTerms];
|
|
2360
|
-
const fields = [...primaryFields, ...secondaryFields];
|
|
2361
|
-
if (!matchesIndexedOrTableScan(fields, normalizedQuery, indexedMatches, indexedMatches.documents, pathValue)) {
|
|
2362
|
-
continue;
|
|
2363
|
-
}
|
|
2364
|
-
results.push(withCacheHint({
|
|
2365
|
-
kind: 'document',
|
|
2366
|
-
path: pathValue,
|
|
2367
|
-
title,
|
|
2368
|
-
document_type: typeValue,
|
|
2369
|
-
...workflowAuthorityForDocument(typeValue),
|
|
2370
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2371
|
-
score: scoreIndexedOrTableScan(primaryFields, secondaryFields, normalizedQuery, indexedMatches, indexedMatches.documents, pathValue),
|
|
2372
|
-
}, cacheLayers));
|
|
2373
|
-
}
|
|
2374
|
-
for (const row of queryCandidateRows(database, 'SELECT name, path, title FROM skills', 'name', indexedMatches.skills, indexedMatches)) {
|
|
2375
|
-
const name = toSearchString(row.name);
|
|
2376
|
-
const pathValue = toSearchString(row.path);
|
|
2377
|
-
const title = toSearchString(row.title);
|
|
2378
|
-
const fields = [name, pathValue, title];
|
|
2379
|
-
if (!matchesIndexedOrTableScan(fields, normalizedQuery, indexedMatches, indexedMatches.skills, name)) {
|
|
2380
|
-
continue;
|
|
2381
|
-
}
|
|
2382
|
-
results.push(withCacheHint({
|
|
2383
|
-
kind: 'skill',
|
|
2384
|
-
name,
|
|
2385
|
-
path: pathValue,
|
|
2386
|
-
title,
|
|
2387
|
-
...skillAuthority(),
|
|
2388
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2389
|
-
score: scoreIndexedOrTableScan([name, pathValue, title], [], normalizedQuery, indexedMatches, indexedMatches.skills, name),
|
|
2390
|
-
}, cacheLayers));
|
|
2391
|
-
}
|
|
2392
|
-
const matchedSkillRouteNames = new Set([...indexedMatches.skillRoutes].map((routeKey) => routeKey.split('\u0000')[0] ?? ''));
|
|
2393
|
-
for (const row of queryCandidateRows(database, 'SELECT skill_name, skill_path, trigger, required_input, edit_scope, risk, verification_intents, expected_output FROM skill_routes', 'skill_name', matchedSkillRouteNames, indexedMatches)) {
|
|
2394
|
-
const name = toSearchString(row.skill_name);
|
|
2395
|
-
const pathValue = toSearchString(row.skill_path);
|
|
2396
|
-
const trigger = toSearchString(row.trigger);
|
|
2397
|
-
const requiredInput = toSearchString(row.required_input);
|
|
2398
|
-
const editScope = toSearchString(row.edit_scope);
|
|
2399
|
-
const risk = toSearchString(row.risk);
|
|
2400
|
-
const verificationIntents = splitVerificationIntents(toSearchString(row.verification_intents));
|
|
2401
|
-
const expectedOutput = toSearchString(row.expected_output);
|
|
2402
|
-
const primaryFields = [name, trigger];
|
|
2403
|
-
const secondaryFields = [pathValue, requiredInput, editScope, risk, expectedOutput];
|
|
2404
|
-
const fields = [...primaryFields, ...secondaryFields];
|
|
2405
|
-
const routeKey = skillRouteKey({ skillName: name, trigger });
|
|
2406
|
-
const indexedRouteMatch = indexedMatches.active && indexedMatches.skillRoutes.has(routeKey);
|
|
2407
|
-
if (!indexedRouteMatch && !isMatched(fields, normalizedQuery)) {
|
|
2408
|
-
continue;
|
|
2409
|
-
}
|
|
2410
|
-
results.push(withCacheHint({
|
|
2411
|
-
kind: 'skill_route',
|
|
2412
|
-
name,
|
|
2413
|
-
path: pathValue,
|
|
2414
|
-
title: name,
|
|
2415
|
-
route_trigger: trigger,
|
|
2416
|
-
route_risk: risk,
|
|
2417
|
-
verification_intents: verificationIntents,
|
|
2418
|
-
...skillAuthority(),
|
|
2419
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2420
|
-
score: indexedRouteMatch
|
|
2421
|
-
? Math.max(scoreMatch(primaryFields, secondaryFields, normalizedQuery), 20)
|
|
2422
|
-
: scoreMatch(primaryFields, secondaryFields, normalizedQuery),
|
|
2423
|
-
}, cacheLayers));
|
|
2424
|
-
}
|
|
2425
|
-
const commandRows = queryCandidateRows(database, 'SELECT name, status, lifecycle, run_policy, description FROM command_intents', 'name', indexedMatches.commandIntents, indexedMatches);
|
|
2426
|
-
const effectsByIntent = getCommandEffectsByIntent(database, commandRows.map((row) => toSearchString(row.name)));
|
|
2427
|
-
for (const row of commandRows) {
|
|
2428
|
-
const name = toSearchString(row.name);
|
|
2429
|
-
const status = toSearchString(row.status);
|
|
2430
|
-
const lifecycle = toSearchString(row.lifecycle);
|
|
2431
|
-
const runPolicy = toSearchString(row.run_policy);
|
|
2432
|
-
const description = toSearchString(row.description);
|
|
2433
|
-
const effects = effectsByIntent.get(name) ?? [];
|
|
2434
|
-
const effectLocks = [...new Set(effects.map((effect) => effect.lock))].sort((left, right) => left.localeCompare(right));
|
|
2435
|
-
const effectPaths = [
|
|
2436
|
-
...new Set(effects.map((effect) => effect.path).filter((effectPath) => effectPath !== null)),
|
|
2437
|
-
].sort((left, right) => left.localeCompare(right));
|
|
2438
|
-
const effectModes = [...new Set(effects.map((effect) => effect.mode))].sort((left, right) => left.localeCompare(right));
|
|
2439
|
-
const primaryFields = [name];
|
|
2440
|
-
const secondaryFields = [status, lifecycle, runPolicy, description, ...effectLocks, ...effectPaths, ...effectModes];
|
|
2441
|
-
const fields = [...primaryFields, ...secondaryFields];
|
|
2442
|
-
if (!matchesIndexedOrTableScan(fields, normalizedQuery, indexedMatches, indexedMatches.commandIntents, name)) {
|
|
2443
|
-
continue;
|
|
2444
|
-
}
|
|
2445
|
-
results.push(withCacheHint({
|
|
2446
|
-
kind: 'command_intent',
|
|
2447
|
-
name,
|
|
2448
|
-
title: description || name,
|
|
2449
|
-
effect_locks: effectLocks,
|
|
2450
|
-
effect_paths: effectPaths,
|
|
2451
|
-
effect_modes: effectModes,
|
|
2452
|
-
...commandIntentAuthority(),
|
|
2453
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2454
|
-
score: scoreIndexedOrTableScan(primaryFields, secondaryFields, normalizedQuery, indexedMatches, indexedMatches.commandIntents, name),
|
|
2455
|
-
}, cacheLayers));
|
|
2456
|
-
}
|
|
2457
|
-
}
|
|
2458
|
-
if (scope === 'source' || scope === 'all') {
|
|
2459
|
-
for (const row of queryCandidateRows(database, 'SELECT source_anchors.id, path, line_start, purpose, search_terms, invariant, risk, source_anchors.navigation_only, source_anchors.can_instruct_agent, status, confidence FROM source_anchors LEFT JOIN source_anchor_status ON source_anchor_status.anchor_id = source_anchors.id', 'source_anchors.id', indexedMatches.sourceAnchors, indexedMatches)) {
|
|
2460
|
-
const id = toSearchString(row.id);
|
|
2461
|
-
const pathValue = toSearchString(row.path);
|
|
2462
|
-
const purpose = toSearchString(row.purpose);
|
|
2463
|
-
const searchTerms = toSearchString(row.search_terms);
|
|
2464
|
-
const invariant = toSearchString(row.invariant);
|
|
2465
|
-
const risk = toSearchString(row.risk);
|
|
2466
|
-
const primaryFields = [id, pathValue];
|
|
2467
|
-
const secondaryFields = [purpose, searchTerms, invariant, risk];
|
|
2468
|
-
const fields = [...primaryFields, ...secondaryFields];
|
|
2469
|
-
if (!matchesIndexedOrTableScan(fields, normalizedQuery, indexedMatches, indexedMatches.sourceAnchors, id)) {
|
|
2470
|
-
continue;
|
|
2471
|
-
}
|
|
2472
|
-
results.push(withCacheHint({
|
|
2473
|
-
kind: 'source_anchor',
|
|
2474
|
-
anchor_id: id,
|
|
2475
|
-
name: id,
|
|
2476
|
-
path: pathValue,
|
|
2477
|
-
line_start: Number(row.line_start),
|
|
2478
|
-
title: purpose || id,
|
|
2479
|
-
risk,
|
|
2480
|
-
...sourceAnchorAuthority(),
|
|
2481
|
-
stale_status: toSearchString(row.status),
|
|
2482
|
-
stale_confidence: Number(row.confidence),
|
|
2483
|
-
match: getMatchSnippet(fields, normalizedQuery),
|
|
2484
|
-
score: scoreIndexedOrTableScan(primaryFields, secondaryFields, normalizedQuery, indexedMatches, indexedMatches.sourceAnchors, id),
|
|
2485
|
-
}, cacheLayers));
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
catch (error) {
|
|
2490
|
-
if (isLocalIndexStaleError(error)) {
|
|
2491
|
-
throw error;
|
|
2492
|
-
}
|
|
2493
|
-
if (isLocalIndexRuntimeUnavailableError(error)) {
|
|
2494
|
-
return searchLocalWorkflowFilesDirectly(projectRoot, databasePath, normalizedQuery, limit, scope);
|
|
2495
|
-
}
|
|
2496
|
-
throw error;
|
|
2497
|
-
}
|
|
2498
|
-
finally {
|
|
2499
|
-
database.close();
|
|
2500
|
-
}
|
|
2501
|
-
const sortedResults = sortLocalSearchResults(results, scope, limit);
|
|
2502
|
-
return {
|
|
2503
|
-
schema_version: LOCAL_INDEX_SCHEMA_VERSION,
|
|
2504
|
-
command: 'search',
|
|
2505
|
-
ok: true,
|
|
2506
|
-
mustflow_root: path.resolve(projectRoot),
|
|
2507
|
-
database_path: databasePath,
|
|
2508
|
-
query: normalizedQuery,
|
|
2509
|
-
limit,
|
|
2510
|
-
scope,
|
|
2511
|
-
index_fresh: true,
|
|
2512
|
-
stale_paths: [],
|
|
2513
|
-
search_backend: capabilities.backend,
|
|
2514
|
-
search_fts5_available: capabilities.fts5Available,
|
|
2515
|
-
result_count: sortedResults.length,
|
|
2516
|
-
results: sortedResults,
|
|
2517
|
-
};
|
|
2518
|
-
}
|