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.
Files changed (75) hide show
  1. package/README.md +12 -2
  2. package/dist/cli/commands/adapters.js +11 -9
  3. package/dist/cli/commands/api.js +263 -113
  4. package/dist/cli/commands/check.js +11 -7
  5. package/dist/cli/commands/classify.js +16 -42
  6. package/dist/cli/commands/context.js +18 -31
  7. package/dist/cli/commands/contract-lint.js +12 -7
  8. package/dist/cli/commands/dashboard.js +65 -114
  9. package/dist/cli/commands/docs.js +43 -26
  10. package/dist/cli/commands/doctor.js +11 -7
  11. package/dist/cli/commands/evidence.js +642 -0
  12. package/dist/cli/commands/explain-verify.js +1 -59
  13. package/dist/cli/commands/explain.js +84 -36
  14. package/dist/cli/commands/handoff.js +13 -17
  15. package/dist/cli/commands/impact.js +14 -20
  16. package/dist/cli/commands/index.js +15 -9
  17. package/dist/cli/commands/init.js +56 -70
  18. package/dist/cli/commands/line-endings.js +15 -9
  19. package/dist/cli/commands/map.js +30 -42
  20. package/dist/cli/commands/next.js +300 -0
  21. package/dist/cli/commands/onboard.js +136 -0
  22. package/dist/cli/commands/run.js +47 -42
  23. package/dist/cli/commands/search.js +43 -69
  24. package/dist/cli/commands/status.js +9 -6
  25. package/dist/cli/commands/update.js +16 -10
  26. package/dist/cli/commands/upgrade.js +9 -6
  27. package/dist/cli/commands/verify/args.js +55 -249
  28. package/dist/cli/commands/verify.js +2 -1
  29. package/dist/cli/commands/version-sources.js +9 -6
  30. package/dist/cli/commands/version.js +9 -6
  31. package/dist/cli/commands/workspace.js +564 -0
  32. package/dist/cli/i18n/en.js +60 -1
  33. package/dist/cli/i18n/es.js +60 -1
  34. package/dist/cli/i18n/fr.js +60 -1
  35. package/dist/cli/i18n/hi.js +60 -1
  36. package/dist/cli/i18n/ko.js +60 -1
  37. package/dist/cli/i18n/zh.js +60 -1
  38. package/dist/cli/index.js +28 -25
  39. package/dist/cli/lib/agent-context.js +8 -9
  40. package/dist/cli/lib/command-registry.js +24 -0
  41. package/dist/cli/lib/dashboard-html/client-script.js +1 -1
  42. package/dist/cli/lib/local-index/database-path.js +5 -0
  43. package/dist/cli/lib/local-index/database-read.js +88 -0
  44. package/dist/cli/lib/local-index/effect-graph-read-model.js +112 -0
  45. package/dist/cli/lib/local-index/freshness.js +60 -0
  46. package/dist/cli/lib/local-index/index.js +12 -1866
  47. package/dist/cli/lib/local-index/path-surface-read-model.js +134 -0
  48. package/dist/cli/lib/local-index/populate.js +474 -0
  49. package/dist/cli/lib/local-index/schema.js +413 -0
  50. package/dist/cli/lib/local-index/search-read-model.js +533 -0
  51. package/dist/cli/lib/local-index/search-text.js +79 -0
  52. package/dist/cli/lib/option-parser.js +93 -0
  53. package/dist/cli/lib/repo-map.js +2 -2
  54. package/dist/cli/lib/run-plan.js +5 -22
  55. package/dist/core/change-verification.js +11 -5
  56. package/dist/core/command-effects.js +1 -3
  57. package/dist/core/command-intent-eligibility.js +14 -0
  58. package/dist/core/command-preconditions.js +8 -4
  59. package/dist/core/command-run-constraints.js +43 -0
  60. package/dist/core/public-json-contracts.js +57 -0
  61. package/dist/core/test-selection.js +8 -2
  62. package/dist/core/verification-plan.js +32 -4
  63. package/package.json +1 -1
  64. package/schemas/README.md +16 -0
  65. package/schemas/api-serve-response.schema.json +89 -0
  66. package/schemas/change-verification-report.schema.json +4 -1
  67. package/schemas/contract-lint-report.schema.json +1 -0
  68. package/schemas/evidence-report.schema.json +287 -0
  69. package/schemas/explain-report.schema.json +4 -0
  70. package/schemas/next-report.schema.json +121 -0
  71. package/schemas/onboard-commands-report.schema.json +100 -0
  72. package/schemas/workspace-command-catalog.schema.json +172 -0
  73. package/schemas/workspace-status.schema.json +141 -0
  74. package/schemas/workspace-verification-plan.schema.json +195 -0
  75. package/templates/default/manifest.toml +1 -1
@@ -0,0 +1,134 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { toPosixPath } from '../filesystem.js';
3
+ import { getLocalIndexDatabasePath } from './database-path.js';
4
+ import { queryRows, toSearchString } from './database-read.js';
5
+ import { getStalePaths } from './freshness.js';
6
+ import { loadSqlJs } from './sql.js';
7
+ function createPathSurfaceReadModelStatus(databasePath, status, inputPath, stalePaths = []) {
8
+ return {
9
+ source: 'local_index',
10
+ status,
11
+ databasePath,
12
+ indexFresh: status === 'fresh',
13
+ stalePaths,
14
+ inputPath,
15
+ match: null,
16
+ refreshHint: status === 'fresh' ? null : 'Run `mf index` to refresh path-surface explanations.',
17
+ };
18
+ }
19
+ function pathSurfaceReadModelWithMatch(base, match) {
20
+ return {
21
+ ...base,
22
+ match,
23
+ };
24
+ }
25
+ function readPathSurfaceReasonMap(database) {
26
+ const byRule = new Map();
27
+ for (const row of queryRows(database, 'SELECT rule_id, reason_kind, reason FROM path_surface_reasons ORDER BY rule_id, reason_kind, ordinal')) {
28
+ const ruleId = toSearchString(row.rule_id);
29
+ const reasonKind = toSearchString(row.reason_kind);
30
+ const reason = toSearchString(row.reason);
31
+ let reasonsByKind = byRule.get(ruleId);
32
+ if (!reasonsByKind) {
33
+ reasonsByKind = new Map();
34
+ byRule.set(ruleId, reasonsByKind);
35
+ }
36
+ const reasons = reasonsByKind.get(reasonKind) ?? [];
37
+ reasons.push(reason);
38
+ reasonsByKind.set(reasonKind, reasons);
39
+ }
40
+ return byRule;
41
+ }
42
+ function readPathSurfaceRuleMatches(database) {
43
+ const reasons = readPathSurfaceReasonMap(database);
44
+ 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) => {
45
+ const ruleId = toSearchString(row.rule_id);
46
+ const reasonsByKind = reasons.get(ruleId);
47
+ const reasonList = (kind) => reasonsByKind?.get(kind) ?? [];
48
+ return {
49
+ ruleId,
50
+ patternKind: toSearchString(row.pattern_kind),
51
+ pattern: toSearchString(row.pattern),
52
+ patternFlags: toSearchString(row.pattern_flags),
53
+ changeKinds: reasonList('change_kind'),
54
+ surface: {
55
+ kind: toSearchString(row.surface_kind),
56
+ category: toSearchString(row.category),
57
+ isPublicSurface: Number(row.is_public_surface) === 1,
58
+ validationReasons: reasonList('validation_reason'),
59
+ affectedContracts: reasonList('affected_contract'),
60
+ updatePolicy: toSearchString(row.update_policy),
61
+ driftChecks: reasonList('drift_check'),
62
+ },
63
+ };
64
+ });
65
+ }
66
+ function matchPathSurfaceRule(relativePath, rules) {
67
+ if (!relativePath) {
68
+ return null;
69
+ }
70
+ for (const rule of rules) {
71
+ try {
72
+ if (new RegExp(rule.pattern, rule.patternFlags).test(relativePath)) {
73
+ return rule;
74
+ }
75
+ }
76
+ catch {
77
+ continue;
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+ export async function readLocalPathSurfaces(projectRoot, relativePaths) {
83
+ const databasePath = getLocalIndexDatabasePath(projectRoot);
84
+ const normalizedPaths = [...new Set(relativePaths.map((relativePath) => toPosixPath(relativePath)).filter(Boolean))];
85
+ const statusMap = (status, stalePaths = []) => new Map(normalizedPaths.map((relativePath) => [
86
+ relativePath,
87
+ createPathSurfaceReadModelStatus(databasePath, status, relativePath, stalePaths),
88
+ ]));
89
+ if (!existsSync(databasePath)) {
90
+ return statusMap('missing');
91
+ }
92
+ const SQL = await loadSqlJs();
93
+ const database = new SQL.Database(readFileSync(databasePath));
94
+ try {
95
+ const stalePaths = getStalePaths(projectRoot, database);
96
+ if (stalePaths.length > 0) {
97
+ return statusMap('stale', stalePaths);
98
+ }
99
+ const rules = readPathSurfaceRuleMatches(database);
100
+ return new Map(normalizedPaths.map((relativePath) => {
101
+ const base = createPathSurfaceReadModelStatus(databasePath, 'fresh', relativePath);
102
+ return [relativePath, pathSurfaceReadModelWithMatch(base, matchPathSurfaceRule(relativePath, rules))];
103
+ }));
104
+ }
105
+ catch {
106
+ return statusMap('unreadable');
107
+ }
108
+ finally {
109
+ database.close();
110
+ }
111
+ }
112
+ export async function readLocalPathSurface(projectRoot, relativePath) {
113
+ const databasePath = getLocalIndexDatabasePath(projectRoot);
114
+ const inputPath = relativePath ? toPosixPath(relativePath) : null;
115
+ if (!inputPath) {
116
+ if (!existsSync(databasePath)) {
117
+ return createPathSurfaceReadModelStatus(databasePath, 'missing', null);
118
+ }
119
+ const SQL = await loadSqlJs();
120
+ const database = new SQL.Database(readFileSync(databasePath));
121
+ try {
122
+ const stalePaths = getStalePaths(projectRoot, database);
123
+ return createPathSurfaceReadModelStatus(databasePath, stalePaths.length > 0 ? 'stale' : 'fresh', null, stalePaths);
124
+ }
125
+ catch {
126
+ return createPathSurfaceReadModelStatus(databasePath, 'unreadable', null);
127
+ }
128
+ finally {
129
+ database.close();
130
+ }
131
+ }
132
+ const surfaces = await readLocalPathSurfaces(projectRoot, [inputPath]);
133
+ return surfaces.get(inputPath) ?? createPathSurfaceReadModelStatus(databasePath, 'unreadable', inputPath);
134
+ }
@@ -0,0 +1,474 @@
1
+ import { listChangeClassificationRuleDescriptors } from '../../../core/change-classification.js';
2
+ 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, SEARCH_BACKEND_FTS5, SEARCH_NGRAM_MAX_GRAMS_PER_TARGET, SEARCH_NGRAM_MAX_TOKEN_CHARS, SOURCE_INDEX_MAX_FILE_BYTES, } from './constants.js';
3
+ import { queryRows, toSearchString } from './database-read.js';
4
+ import { sha256Text } from './hashing.js';
5
+ import { buildSearchNgrams, normalizeSearchText } from './search-text.js';
6
+ import { skillRouteKey } from './workflow-documents.js';
7
+ function joinedList(values) {
8
+ return [...values].sort((left, right) => left.localeCompare(right)).join(', ');
9
+ }
10
+ function insertDocumentTerm(database, documentPath, term, source) {
11
+ const normalized = normalizeSearchText(term ?? '');
12
+ if (normalized.length === 0) {
13
+ return;
14
+ }
15
+ database.run('INSERT OR IGNORE INTO document_terms (document_path, term, source) VALUES (?, ?, ?)', [
16
+ documentPath,
17
+ normalized,
18
+ source,
19
+ ]);
20
+ }
21
+ function insertSearchNgrams(database, targetKind, targetKey, values, source) {
22
+ for (const gram of buildSearchNgrams(values)) {
23
+ database.run('INSERT OR IGNORE INTO search_ngrams (target_kind, target_key, gram, source) VALUES (?, ?, ?, ?)', [targetKind, targetKey, gram, source]);
24
+ }
25
+ }
26
+ function insertPathSurfaceReasons(database, ruleId, reasonKind, values) {
27
+ values.forEach((value, index) => {
28
+ database.run('INSERT INTO path_surface_reasons (rule_id, reason_kind, reason, ordinal) VALUES (?, ?, ?, ?)', [ruleId, reasonKind, value, index + 1]);
29
+ });
30
+ }
31
+ function populatePathSurfaceReadModel(database) {
32
+ for (const rule of listChangeClassificationRuleDescriptors()) {
33
+ database.run('INSERT INTO path_surfaces (rule_id, pattern_kind, pattern, pattern_flags, surface_kind, category, is_public_surface, update_policy) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
34
+ rule.id,
35
+ rule.patternKind,
36
+ rule.pattern,
37
+ rule.patternFlags,
38
+ rule.surface.kind,
39
+ rule.surface.category,
40
+ rule.surface.isPublicSurface ? 1 : 0,
41
+ rule.surface.updatePolicy,
42
+ ]);
43
+ insertPathSurfaceReasons(database, rule.id, 'change_kind', rule.changeKinds);
44
+ insertPathSurfaceReasons(database, rule.id, 'validation_reason', rule.surface.validationReasons);
45
+ insertPathSurfaceReasons(database, rule.id, 'affected_contract', rule.surface.affectedContracts);
46
+ insertPathSurfaceReasons(database, rule.id, 'drift_check', rule.surface.driftChecks);
47
+ }
48
+ }
49
+ function populateSearchTables(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors) {
50
+ for (const document of documents) {
51
+ const documentTerms = queryRows(database, 'SELECT term FROM document_terms WHERE document_path = ? ORDER BY term', [
52
+ document.path,
53
+ ]).map((row) => toSearchString(row.term));
54
+ insertSearchNgrams(database, 'document', document.path, [
55
+ document.path,
56
+ document.type,
57
+ document.title,
58
+ document.sections.join(' '),
59
+ documentTerms.join(' '),
60
+ document.contentSnippet,
61
+ ], 'workflow_document');
62
+ if (capabilities.backend === SEARCH_BACKEND_FTS5) {
63
+ database.run('INSERT INTO search_documents_fts (path, type, title, sections, terms, snippet) VALUES (?, ?, ?, ?, ?, ?)', [
64
+ document.path,
65
+ document.type,
66
+ document.title,
67
+ document.sections.join(' '),
68
+ documentTerms.join(' '),
69
+ document.contentSnippet,
70
+ ]);
71
+ }
72
+ }
73
+ for (const skill of skills) {
74
+ insertSearchNgrams(database, 'skill', skill.name, [skill.name, skill.path, skill.title], 'skill');
75
+ if (capabilities.backend === SEARCH_BACKEND_FTS5) {
76
+ database.run('INSERT INTO search_skills_fts (name, path, title) VALUES (?, ?, ?)', [
77
+ skill.name,
78
+ skill.path,
79
+ skill.title,
80
+ ]);
81
+ }
82
+ }
83
+ for (const route of skillRoutes) {
84
+ const verificationIntents = route.verificationIntents.join(' ');
85
+ insertSearchNgrams(database, 'skill_route', skillRouteKey(route), [
86
+ skillRouteKey(route),
87
+ route.skillName,
88
+ route.skillPath,
89
+ route.trigger,
90
+ route.requiredInput,
91
+ route.editScope,
92
+ route.risk,
93
+ verificationIntents,
94
+ route.expectedOutput,
95
+ ], 'skill_route');
96
+ if (capabilities.backend === SEARCH_BACKEND_FTS5) {
97
+ 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 (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
98
+ skillRouteKey(route),
99
+ route.skillName,
100
+ route.skillPath,
101
+ route.trigger,
102
+ route.requiredInput,
103
+ route.editScope,
104
+ route.risk,
105
+ verificationIntents,
106
+ route.expectedOutput,
107
+ ]);
108
+ }
109
+ }
110
+ for (const intent of commandIntents) {
111
+ const effects = intent.effects
112
+ .flatMap((effect) => [effect.lock, effect.path ?? '', effect.mode, effect.access, effect.concurrency])
113
+ .join(' ');
114
+ insertSearchNgrams(database, 'command_intent', intent.name, [intent.name, intent.status, intent.lifecycle ?? '', intent.runPolicy ?? '', intent.description ?? '', effects], 'command_intent');
115
+ if (capabilities.backend === SEARCH_BACKEND_FTS5) {
116
+ 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]);
117
+ }
118
+ }
119
+ for (const anchor of sourceAnchors) {
120
+ insertSearchNgrams(database, 'source_anchor', anchor.id, [
121
+ anchor.id,
122
+ anchor.path,
123
+ anchor.purpose ?? '',
124
+ anchor.search.join(' '),
125
+ anchor.invariant ?? '',
126
+ anchor.risk.join(' '),
127
+ ], 'source_anchor');
128
+ if (capabilities.backend === SEARCH_BACKEND_FTS5) {
129
+ database.run('INSERT INTO search_source_anchors_fts (id, path, purpose, search_terms, invariant, risk) VALUES (?, ?, ?, ?, ?, ?)', [
130
+ anchor.id,
131
+ anchor.path,
132
+ anchor.purpose,
133
+ anchor.search.join(' '),
134
+ anchor.invariant,
135
+ anchor.risk.join(' '),
136
+ ]);
137
+ }
138
+ }
139
+ }
140
+ export function createSourceAnchorRiskSignals(sourceAnchors) {
141
+ return sourceAnchors
142
+ .filter((anchor) => ['changed', 'review', 'stale'].includes(anchor.status))
143
+ .map((anchor) => ({
144
+ anchorId: anchor.id,
145
+ pathHash: sha256Text(anchor.path),
146
+ status: anchor.status,
147
+ riskSignal: anchor.signals.risk,
148
+ confidence: anchor.confidence,
149
+ navigationOnly: anchor.navigationOnly,
150
+ canInstructAgent: anchor.canInstructAgent,
151
+ }));
152
+ }
153
+ function rollbackTransaction(database) {
154
+ try {
155
+ database.run('ROLLBACK');
156
+ }
157
+ catch {
158
+ // Keep the original indexing failure as the actionable error.
159
+ }
160
+ }
161
+ export function populateDatabase(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors, indexedFiles, verificationEvidence, indexMode, sourceScopeHash, sourceIndexEnabled, indexedAt) {
162
+ const sourceAnchorRiskSignals = createSourceAnchorRiskSignals(sourceAnchors);
163
+ database.run('BEGIN TRANSACTION');
164
+ try {
165
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['schema_version', LOCAL_INDEX_SCHEMA_VERSION]);
166
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['parser_version', LOCAL_INDEX_PARSER_VERSION]);
167
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['content_mode', LOCAL_INDEX_CONTENT_MODE]);
168
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
169
+ 'store_full_content',
170
+ String(LOCAL_INDEX_STORE_FULL_CONTENT),
171
+ ]);
172
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
173
+ 'max_snippet_bytes_per_document',
174
+ String(MAX_SNIPPET_BYTES_PER_DOCUMENT),
175
+ ]);
176
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
177
+ 'search_ngram_max_token_chars',
178
+ String(SEARCH_NGRAM_MAX_TOKEN_CHARS),
179
+ ]);
180
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
181
+ 'search_ngram_max_grams_per_target',
182
+ String(SEARCH_NGRAM_MAX_GRAMS_PER_TARGET),
183
+ ]);
184
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
185
+ 'source_index_max_file_bytes',
186
+ String(SOURCE_INDEX_MAX_FILE_BYTES),
187
+ ]);
188
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
189
+ 'excluded_raw_data_kinds',
190
+ LOCAL_INDEX_EXCLUDED_RAW_DATA_KINDS.join(','),
191
+ ]);
192
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['search_backend', capabilities.backend]);
193
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', [
194
+ 'search_fts5_available',
195
+ String(capabilities.fts5Available),
196
+ ]);
197
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['source_scope_hash', sourceScopeHash]);
198
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['source_index_enabled', String(sourceIndexEnabled)]);
199
+ database.run('INSERT INTO metadata (key, value) VALUES (?, ?)', ['index_mode', indexMode]);
200
+ for (const indexedFile of indexedFiles) {
201
+ database.run('INSERT INTO indexed_files (path, source_scope, size_bytes, mtime_ms, content_hash, indexed_at, index_mode, parser_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
202
+ indexedFile.path,
203
+ indexedFile.sourceScope,
204
+ indexedFile.sizeBytes,
205
+ indexedFile.mtimeMs,
206
+ indexedFile.contentHash,
207
+ indexedAt,
208
+ indexMode,
209
+ LOCAL_INDEX_PARSER_VERSION,
210
+ ]);
211
+ }
212
+ for (const document of documents) {
213
+ database.run('INSERT INTO documents (path, type, title, locale, revision, content_hash, content_snippet) VALUES (?, ?, ?, ?, ?, ?, ?)', [
214
+ document.path,
215
+ document.type,
216
+ document.title,
217
+ document.locale,
218
+ document.revision,
219
+ document.contentHash,
220
+ document.contentSnippet,
221
+ ]);
222
+ document.sections.forEach((heading, index) => {
223
+ database.run('INSERT INTO sections (document_path, ordinal, heading) VALUES (?, ?, ?)', [
224
+ document.path,
225
+ index + 1,
226
+ heading,
227
+ ]);
228
+ });
229
+ }
230
+ for (const skill of skills) {
231
+ database.run('INSERT INTO skills (name, path, title) VALUES (?, ?, ?)', [skill.name, skill.path, skill.title]);
232
+ }
233
+ for (const route of skillRoutes) {
234
+ const verificationIntents = route.verificationIntents.join(', ');
235
+ database.run('INSERT INTO skill_routes (skill_name, skill_path, trigger, required_input, edit_scope, risk, verification_intents, expected_output) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
236
+ route.skillName,
237
+ route.skillPath,
238
+ route.trigger,
239
+ route.requiredInput,
240
+ route.editScope,
241
+ route.risk,
242
+ verificationIntents,
243
+ route.expectedOutput,
244
+ ]);
245
+ insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.skillName, 'skill_route_name');
246
+ insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.trigger, 'skill_route_trigger');
247
+ insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.risk, 'skill_route_risk');
248
+ insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.requiredInput, 'skill_route_required_input');
249
+ insertDocumentTerm(database, '.mustflow/skills/INDEX.md', route.editScope, 'skill_route_edit_scope');
250
+ insertDocumentTerm(database, '.mustflow/skills/INDEX.md', verificationIntents, 'skill_route_verification_intents');
251
+ }
252
+ for (const intent of commandIntents) {
253
+ database.run('INSERT INTO command_intents (name, status, lifecycle, run_policy, description) VALUES (?, ?, ?, ?, ?)', [intent.name, intent.status, intent.lifecycle, intent.runPolicy, intent.description]);
254
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.name, 'command_intent');
255
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.status, 'command_status');
256
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.lifecycle, 'command_lifecycle');
257
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.runPolicy, 'command_run_policy');
258
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', intent.description, 'command_description');
259
+ for (const effect of intent.effects) {
260
+ 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]);
261
+ if (effect.path !== null) {
262
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', effect.path, 'command_effect_path');
263
+ }
264
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', effect.lock, 'command_effect_lock');
265
+ insertDocumentTerm(database, '.mustflow/config/commands.toml', effect.mode, 'command_effect_mode');
266
+ }
267
+ }
268
+ for (const anchor of sourceAnchors) {
269
+ database.run('INSERT INTO source_anchors (id, path, line_start, purpose, search_terms, invariant, risk, navigation_only, can_instruct_agent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
270
+ anchor.id,
271
+ anchor.path,
272
+ anchor.lineStart,
273
+ anchor.purpose,
274
+ anchor.search.join(', '),
275
+ anchor.invariant,
276
+ anchor.risk.join(', '),
277
+ anchor.navigationOnly ? 1 : 0,
278
+ anchor.canInstructAgent ? 1 : 0,
279
+ ]);
280
+ 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
281
+ anchor.id,
282
+ anchor.path,
283
+ anchor.lineStart,
284
+ anchor.fingerprint.anchorMetadataHash,
285
+ anchor.fingerprint.anchorTextHash,
286
+ anchor.fingerprint.contextHash,
287
+ anchor.fingerprint.searchTermsHash,
288
+ anchor.fingerprint.invariantHash,
289
+ anchor.fingerprint.riskHash,
290
+ anchor.fingerprint.symbol.kind,
291
+ anchor.fingerprint.symbol.name,
292
+ anchor.fingerprint.symbol.exported ? 1 : 0,
293
+ anchor.fingerprint.symbol.signatureHash,
294
+ anchor.fingerprint.symbol.bodyHash,
295
+ anchor.fingerprint.symbol.startLine,
296
+ anchor.fingerprint.symbol.endLine,
297
+ ]);
298
+ 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
299
+ anchor.id,
300
+ anchor.status,
301
+ anchor.confidence,
302
+ anchor.signals.identity,
303
+ anchor.signals.location,
304
+ anchor.signals.symbol,
305
+ anchor.signals.body,
306
+ anchor.signals.metadata,
307
+ anchor.signals.semantic,
308
+ anchor.signals.risk,
309
+ anchor.navigationOnly ? 1 : 0,
310
+ anchor.canInstructAgent ? 1 : 0,
311
+ ]);
312
+ }
313
+ for (const summary of verificationEvidence.summaries) {
314
+ 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
315
+ summary.sourcePath,
316
+ summary.sourceHash,
317
+ summary.command,
318
+ summary.kind,
319
+ summary.status,
320
+ summary.runDir,
321
+ summary.manifestPath,
322
+ summary.verificationPlanId,
323
+ summary.completionStatus,
324
+ summary.primaryReason,
325
+ summary.matchedIntents,
326
+ summary.ranIntents,
327
+ summary.passedIntents,
328
+ summary.failedIntents,
329
+ summary.skippedIntents,
330
+ summary.receiptCount,
331
+ summary.coverageCount,
332
+ summary.remainingRiskCount,
333
+ summary.failureFingerprint,
334
+ ]);
335
+ }
336
+ for (const plan of verificationEvidence.verificationPlans) {
337
+ database.run('INSERT INTO verification_plans (plan_id, source_path, classification_hash, command_contract_hash, selected_intents_hash, created_at, source_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [
338
+ plan.planId,
339
+ plan.sourcePath,
340
+ plan.classificationHash,
341
+ plan.commandContractHash,
342
+ plan.selectedIntentsHash,
343
+ plan.createdAt,
344
+ plan.sourceHash,
345
+ ]);
346
+ }
347
+ for (const criterion of verificationEvidence.acceptanceCriteria) {
348
+ database.run('INSERT INTO acceptance_criteria (criterion_id, plan_id, source, statement_hash, reason, surface, path_hash) VALUES (?, ?, ?, ?, ?, ?, ?)', [
349
+ criterion.criterionId,
350
+ criterion.planId,
351
+ criterion.source,
352
+ criterion.statementHash,
353
+ criterion.reason,
354
+ criterion.surface,
355
+ criterion.pathHash,
356
+ ]);
357
+ }
358
+ for (const coverage of verificationEvidence.criterionCoverage) {
359
+ 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]);
360
+ }
361
+ for (const receipt of verificationEvidence.receipts) {
362
+ database.run('INSERT INTO verification_receipt_summaries (source_path, ordinal, intent, status, skipped, verification_plan_id, receipt_path, receipt_sha256) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
363
+ receipt.sourcePath,
364
+ receipt.ordinal,
365
+ receipt.intent,
366
+ receipt.status,
367
+ receipt.skipped ? 1 : 0,
368
+ receipt.verificationPlanId,
369
+ receipt.receiptPath,
370
+ receipt.receiptSha256,
371
+ ]);
372
+ }
373
+ for (const receipt of verificationEvidence.commandReceiptSummaries) {
374
+ database.run('INSERT INTO command_receipt_summaries (receipt_hash, plan_id, intent, status, command_fingerprint, contract_fingerprint, current_state_hash, write_drift_status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [
375
+ receipt.receiptHash,
376
+ receipt.planId,
377
+ receipt.intent,
378
+ receipt.status,
379
+ receipt.commandFingerprint,
380
+ receipt.contractFingerprint,
381
+ receipt.currentStateHash,
382
+ receipt.writeDriftStatus,
383
+ ]);
384
+ }
385
+ for (const coverage of verificationEvidence.coverageStates) {
386
+ database.run('INSERT INTO verification_coverage_states (source_path, criterion_id, source, status, requirement_reason, intents, receipt_count, gap_count, source_anchor_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', [
387
+ coverage.sourcePath,
388
+ coverage.criterionId,
389
+ coverage.source,
390
+ coverage.status,
391
+ coverage.requirementReason,
392
+ joinedList(coverage.intents),
393
+ coverage.receiptCount,
394
+ coverage.gapCount,
395
+ coverage.sourceAnchorCount,
396
+ ]);
397
+ }
398
+ for (const risk of verificationEvidence.riskSignals) {
399
+ 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]);
400
+ }
401
+ for (const signal of verificationEvidence.validationRatchetSignals) {
402
+ 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]);
403
+ }
404
+ for (const verdict of verificationEvidence.completionVerdictSummaries) {
405
+ database.run('INSERT INTO completion_verdict_summaries (claim_id, plan_id, status, primary_reason, risk_count, contradiction_count, blocker_count) VALUES (?, ?, ?, ?, ?, ?, ?)', [
406
+ verdict.claimId,
407
+ verdict.planId,
408
+ verdict.status,
409
+ verdict.primaryReason,
410
+ verdict.riskCount,
411
+ verdict.contradictionCount,
412
+ verdict.blockerCount,
413
+ ]);
414
+ }
415
+ for (const route of verificationEvidence.reproRoutes) {
416
+ 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]);
417
+ }
418
+ for (const observation of verificationEvidence.reproObservations) {
419
+ database.run('INSERT INTO repro_observations (route_id, phase, outcome, receipt_hash, diagnostic_fingerprint) VALUES (?, ?, ?, ?, ?)', [
420
+ observation.routeId,
421
+ observation.phase,
422
+ observation.outcome,
423
+ observation.receiptHash,
424
+ observation.diagnosticFingerprint,
425
+ ]);
426
+ }
427
+ for (const fingerprint of verificationEvidence.failureFingerprintReadModels) {
428
+ database.run('INSERT INTO failure_fingerprints (fingerprint, plan_id, failed_intents_hash, risk_codes_hash, seen_count, first_seen_at, last_seen_at) VALUES (?, ?, ?, ?, ?, ?, ?)', [
429
+ fingerprint.fingerprint,
430
+ fingerprint.planId,
431
+ fingerprint.failedIntentsHash,
432
+ fingerprint.riskCodesHash,
433
+ fingerprint.seenCount,
434
+ fingerprint.firstSeenAt,
435
+ fingerprint.lastSeenAt,
436
+ ]);
437
+ }
438
+ for (const fingerprint of verificationEvidence.failureFingerprints) {
439
+ 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', [
440
+ fingerprint.sourcePath,
441
+ fingerprint.fingerprint,
442
+ fingerprint.verificationPlanId,
443
+ fingerprint.status,
444
+ joinedList(fingerprint.failedIntents),
445
+ fingerprint.primaryReason,
446
+ fingerprint.failedIntentsHash,
447
+ fingerprint.riskCodesHash,
448
+ fingerprint.affectedSurfacesHash,
449
+ fingerprint.firstSeenAt,
450
+ fingerprint.lastSeenAt,
451
+ fingerprint.seenCount,
452
+ fingerprint.requiresNewEvidence ? 1 : 0,
453
+ ]);
454
+ }
455
+ for (const signal of sourceAnchorRiskSignals) {
456
+ database.run('INSERT INTO source_anchor_risk_signals (anchor_id, path_hash, status, risk_signal, confidence, navigation_only, can_instruct_agent) VALUES (?, ?, ?, ?, ?, ?, ?)', [
457
+ signal.anchorId,
458
+ signal.pathHash,
459
+ signal.status,
460
+ signal.riskSignal,
461
+ signal.confidence,
462
+ signal.navigationOnly ? 1 : 0,
463
+ signal.canInstructAgent ? 1 : 0,
464
+ ]);
465
+ }
466
+ populatePathSurfaceReadModel(database);
467
+ populateSearchTables(database, capabilities, documents, skills, skillRoutes, commandIntents, sourceAnchors);
468
+ database.run('COMMIT');
469
+ }
470
+ catch (error) {
471
+ rollbackTransaction(database);
472
+ throw error;
473
+ }
474
+ }