@jagilber-org/index-server 1.22.0 → 1.26.1
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/CHANGELOG.md +87 -2
- package/CODE_OF_CONDUCT.md +2 -0
- package/CONTRIBUTING.md +32 -2
- package/README.md +83 -20
- package/SECURITY.md +17 -5
- package/dist/config/dashboardConfig.d.ts +3 -0
- package/dist/config/dashboardConfig.js +3 -0
- package/dist/config/defaultValues.d.ts +1 -1
- package/dist/config/defaultValues.js +1 -1
- package/dist/config/featureConfig.d.ts +2 -0
- package/dist/config/featureConfig.js +6 -1
- package/dist/config/runtimeConfig.d.ts +1 -1
- package/dist/config/runtimeConfig.js +8 -9
- package/dist/dashboard/client/admin.html +173 -54
- package/dist/dashboard/client/css/admin.css +151 -0
- package/dist/dashboard/client/js/admin.auth.js +25 -11
- package/dist/dashboard/client/js/admin.config.js +1 -1
- package/dist/dashboard/client/js/admin.feedback.js +328 -0
- package/dist/dashboard/client/js/admin.graph.js +120 -18
- package/dist/dashboard/client/js/admin.instructions.js +27 -13
- package/dist/dashboard/client/js/admin.logs.js +1 -5
- package/dist/dashboard/client/js/admin.maintenance.js +53 -8
- package/dist/dashboard/client/js/admin.messaging.js +1 -4
- package/dist/dashboard/client/js/admin.overview.js +5 -1
- package/dist/dashboard/client/js/admin.sessions.js +1 -1
- package/dist/dashboard/client/js/admin.utils.js +43 -1
- package/dist/dashboard/client/js/mermaid.min.js +813 -537
- package/dist/dashboard/export/DataExporter.js +2 -1
- package/dist/dashboard/server/AdminPanel.d.ts +3 -0
- package/dist/dashboard/server/AdminPanel.js +132 -35
- package/dist/dashboard/server/ApiRoutes.js +40 -9
- package/dist/dashboard/server/DashboardServer.js +1 -1
- package/dist/dashboard/server/FileMetricsStorage.d.ts +19 -0
- package/dist/dashboard/server/FileMetricsStorage.js +52 -5
- package/dist/dashboard/server/HttpTransport.js +6 -0
- package/dist/dashboard/server/InstanceManager.js +7 -2
- package/dist/dashboard/server/KnowledgeStore.js +7 -2
- package/dist/dashboard/server/MetricsCollector.d.ts +16 -0
- package/dist/dashboard/server/MetricsCollector.js +113 -17
- package/dist/dashboard/server/legacyDashboardHtml.js +7 -2
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.d.ts +1 -1
- package/dist/dashboard/server/middleware/ensureLoadedMiddleware.js +8 -3
- package/dist/dashboard/server/routes/admin.feedback.routes.d.ts +15 -0
- package/dist/dashboard/server/routes/admin.feedback.routes.js +188 -0
- package/dist/dashboard/server/routes/admin.routes.js +35 -27
- package/dist/dashboard/server/routes/alerts.routes.js +4 -3
- package/dist/dashboard/server/routes/api.feedback.routes.js +2 -1
- package/dist/dashboard/server/routes/api.usage.routes.js +8 -7
- package/dist/dashboard/server/routes/embeddings.routes.d.ts +2 -1
- package/dist/dashboard/server/routes/embeddings.routes.js +18 -9
- package/dist/dashboard/server/routes/graph.routes.js +10 -13
- package/dist/dashboard/server/routes/index.d.ts +1 -0
- package/dist/dashboard/server/routes/index.js +74 -39
- package/dist/dashboard/server/routes/instances.routes.js +2 -1
- package/dist/dashboard/server/routes/instructions.routes.js +46 -27
- package/dist/dashboard/server/routes/knowledge.routes.js +4 -3
- package/dist/dashboard/server/routes/logs.routes.js +5 -4
- package/dist/dashboard/server/routes/messaging.routes.js +15 -14
- package/dist/dashboard/server/routes/metrics.routes.js +14 -13
- package/dist/dashboard/server/routes/scripts.routes.js +6 -3
- package/dist/dashboard/server/routes/status.routes.js +25 -6
- package/dist/dashboard/server/routes/synthetic.routes.js +3 -2
- package/dist/dashboard/server/routes/usage.routes.js +2 -1
- package/dist/dashboard/server/utils/escapeHtml.d.ts +1 -0
- package/dist/dashboard/server/utils/escapeHtml.js +11 -0
- package/dist/dashboard/server/utils/pathContainment.d.ts +1 -0
- package/dist/dashboard/server/utils/pathContainment.js +15 -0
- package/dist/dashboard/server/wsInit.js +2 -2
- package/dist/lib/mcpStdioLogging.d.ts +165 -0
- package/dist/lib/mcpStdioLogging.js +287 -0
- package/dist/schemas/index.d.ts +37 -2
- package/dist/schemas/index.js +27 -3
- package/dist/server/backgroundServicesStartup.d.ts +7 -1
- package/dist/server/backgroundServicesStartup.js +25 -8
- package/dist/server/certInit.d.ts +97 -0
- package/dist/server/certInit.js +359 -0
- package/dist/server/certInit.types.d.ts +92 -0
- package/dist/server/certInit.types.js +34 -0
- package/dist/server/handshake/fallbackFrames.d.ts +31 -0
- package/dist/server/handshake/fallbackFrames.js +38 -0
- package/dist/server/handshake/initializeDetector.d.ts +31 -0
- package/dist/server/handshake/initializeDetector.js +88 -0
- package/dist/server/handshake/protocol.d.ts +15 -0
- package/dist/server/handshake/protocol.js +37 -0
- package/dist/server/handshake/readyEmitter.d.ts +6 -0
- package/dist/server/handshake/readyEmitter.js +88 -0
- package/dist/server/handshake/safetyFallbacks.d.ts +1 -0
- package/dist/server/handshake/safetyFallbacks.js +134 -0
- package/dist/server/handshake/stdinSniffer.d.ts +1 -0
- package/dist/server/handshake/stdinSniffer.js +260 -0
- package/dist/server/handshake/tracing.d.ts +16 -0
- package/dist/server/handshake/tracing.js +95 -0
- package/dist/server/handshakeManager.d.ts +23 -23
- package/dist/server/handshakeManager.js +36 -466
- package/dist/server/index-server.d.ts +23 -0
- package/dist/server/index-server.js +194 -9
- package/dist/server/mcpReadOnlySurfaces.d.ts +44 -0
- package/dist/server/mcpReadOnlySurfaces.js +297 -0
- package/dist/server/sdkServer.js +69 -7
- package/dist/server/transport.d.ts +5 -6
- package/dist/server/transport.js +46 -64
- package/dist/server/transportFactory.d.ts +3 -9
- package/dist/server/transportFactory.js +18 -380
- package/dist/services/atomicFs.d.ts +3 -0
- package/dist/services/atomicFs.js +171 -13
- package/dist/services/auditLog.d.ts +17 -2
- package/dist/services/auditLog.js +75 -14
- package/dist/services/bootstrapGating.js +1 -1
- package/dist/services/categoryRules.d.ts +10 -0
- package/dist/services/categoryRules.js +17 -0
- package/dist/services/classificationService.js +7 -5
- package/dist/services/embeddingService.d.ts +27 -11
- package/dist/services/embeddingService.js +51 -14
- package/dist/services/feedbackStorage.d.ts +39 -0
- package/dist/services/feedbackStorage.js +88 -0
- package/dist/services/handlers/instructions.add.js +429 -317
- package/dist/services/handlers/instructions.groom.js +128 -31
- package/dist/services/handlers/instructions.import.js +56 -23
- package/dist/services/handlers/instructions.patch.js +43 -32
- package/dist/services/handlers/instructions.query.js +20 -29
- package/dist/services/handlers/instructions.shared.d.ts +54 -0
- package/dist/services/handlers/instructions.shared.js +126 -1
- package/dist/services/handlers.activation.js +83 -81
- package/dist/services/handlers.dashboardConfig.d.ts +2 -2
- package/dist/services/handlers.dashboardConfig.js +1 -2
- package/dist/services/handlers.diagnostics.js +75 -54
- package/dist/services/handlers.feedback.d.ts +4 -11
- package/dist/services/handlers.feedback.js +11 -333
- package/dist/services/handlers.gates.js +69 -37
- package/dist/services/handlers.graph.js +2 -2
- package/dist/services/handlers.help.js +2 -2
- package/dist/services/handlers.instructionSchema.js +4 -2
- package/dist/services/handlers.integrity.js +42 -22
- package/dist/services/handlers.messaging.js +1 -1
- package/dist/services/handlers.metrics.js +51 -6
- package/dist/services/handlers.prompt.js +10 -2
- package/dist/services/handlers.search.js +94 -44
- package/dist/services/handlers.trace.js +1 -1
- package/dist/services/handlers.usage.js +38 -7
- package/dist/services/indexContext.d.ts +21 -1
- package/dist/services/indexContext.js +267 -82
- package/dist/services/indexLoader.d.ts +1 -0
- package/dist/services/indexLoader.js +28 -8
- package/dist/services/instructionRecordValidation.d.ts +39 -0
- package/dist/services/instructionRecordValidation.js +388 -0
- package/dist/services/instructions.dispatcher.js +4 -4
- package/dist/services/loaderSchemaValidator.d.ts +15 -0
- package/dist/services/loaderSchemaValidator.js +69 -0
- package/dist/services/logger.js +11 -2
- package/dist/services/mcpLogBridge.d.ts +49 -0
- package/dist/services/mcpLogBridge.js +83 -0
- package/dist/services/ownershipService.js +18 -8
- package/dist/services/performanceBaseline.js +23 -22
- package/dist/services/promptReviewService.d.ts +3 -1
- package/dist/services/promptReviewService.js +41 -13
- package/dist/services/regexSafety.d.ts +6 -0
- package/dist/services/regexSafety.js +46 -0
- package/dist/services/seedBootstrap.js +4 -4
- package/dist/services/storage/factory.d.ts +14 -1
- package/dist/services/storage/factory.js +61 -1
- package/dist/services/storage/jsonEmbeddingStore.d.ts +15 -0
- package/dist/services/storage/jsonEmbeddingStore.js +83 -0
- package/dist/services/storage/jsonFileStore.d.ts +3 -1
- package/dist/services/storage/jsonFileStore.js +8 -6
- package/dist/services/storage/migrationEngine.d.ts +13 -0
- package/dist/services/storage/migrationEngine.js +31 -0
- package/dist/services/storage/sqliteEmbeddingStore.d.ts +30 -0
- package/dist/services/storage/sqliteEmbeddingStore.js +222 -0
- package/dist/services/storage/sqliteStore.d.ts +3 -1
- package/dist/services/storage/sqliteStore.js +2 -2
- package/dist/services/storage/types.d.ts +48 -1
- package/dist/services/toolRegistry.js +77 -67
- package/dist/services/toolRegistry.zod.js +89 -86
- package/dist/services/tracing.js +5 -4
- package/dist/utils/envUtils.d.ts +4 -0
- package/dist/utils/envUtils.js +7 -0
- package/dist/utils/memoryMonitor.js +11 -10
- package/package.json +11 -4
- package/schemas/instruction.schema.json +38 -1
- package/scripts/copy-dashboard-assets.mjs +1 -1
- package/scripts/dist/README.md +1 -1
- package/scripts/setup-wizard.mjs +781 -0
- package/server.json +1 -0
- package/dist/externalClientLib.d.ts +0 -1
- package/dist/externalClientLib.js +0 -2
- package/dist/portableClientWrapper.d.ts +0 -1
- package/dist/portableClientWrapper.js +0 -2
- package/dist/services/indexingService.d.ts +0 -1
- package/dist/services/indexingService.js +0 -2
|
@@ -15,11 +15,14 @@ const schemaVersion_1 = require("../../versioning/schemaVersion");
|
|
|
15
15
|
const categoryRules_1 = require("../categoryRules");
|
|
16
16
|
const canonical_1 = require("../canonical");
|
|
17
17
|
const instructions_shared_1 = require("./instructions.shared");
|
|
18
|
+
const loaderSchemaValidator_1 = require("../loaderSchemaValidator");
|
|
19
|
+
const instructionRecordValidation_1 = require("../instructionRecordValidation");
|
|
18
20
|
(0, registry_1.registerHandler)('index_enrich', (0, instructions_shared_1.guard)('index_enrich', () => {
|
|
19
21
|
const st = (0, indexContext_1.ensureLoaded)();
|
|
20
22
|
let rewritten = 0;
|
|
21
23
|
const updated = [];
|
|
22
24
|
const skipped = [];
|
|
25
|
+
const errors = [];
|
|
23
26
|
for (const e of st.list) {
|
|
24
27
|
// Use in-memory state as the raw record (works for both SQLite and JSON backends)
|
|
25
28
|
const raw = { ...e };
|
|
@@ -27,7 +30,7 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
27
30
|
let needs = false;
|
|
28
31
|
const nowIso = new Date().toISOString();
|
|
29
32
|
if (!(typeof raw.sourceHash === 'string' && raw.sourceHash.length > 0)) {
|
|
30
|
-
raw.sourceHash = e.sourceHash ||
|
|
33
|
+
raw.sourceHash = e.sourceHash || (0, instructions_shared_1.computeSourceHash)(String(e.body || ''));
|
|
31
34
|
needs = true;
|
|
32
35
|
}
|
|
33
36
|
if (typeof raw.createdAt === 'string' && raw.createdAt.length === 0) {
|
|
@@ -109,7 +112,11 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
109
112
|
skipped.push(e.id);
|
|
110
113
|
}
|
|
111
114
|
}
|
|
112
|
-
catch {
|
|
115
|
+
catch (err) {
|
|
116
|
+
const detail = err.message || 'unknown';
|
|
117
|
+
errors.push({ id: e.id, error: detail });
|
|
118
|
+
(0, auditLog_1.logAudit)('enrich_entry_error', e.id, { error: detail });
|
|
119
|
+
}
|
|
113
120
|
}
|
|
114
121
|
if (rewritten) {
|
|
115
122
|
(0, indexContext_1.touchIndexVersion)();
|
|
@@ -117,8 +124,10 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
117
124
|
(0, indexContext_1.ensureLoaded)();
|
|
118
125
|
}
|
|
119
126
|
const resp = { rewritten, updated, skipped };
|
|
127
|
+
if (errors.length)
|
|
128
|
+
resp.errors = errors;
|
|
120
129
|
if (rewritten) {
|
|
121
|
-
(0, auditLog_1.logAudit)('enrich', updated, { rewritten, skipped: skipped.length });
|
|
130
|
+
(0, auditLog_1.logAudit)('enrich', updated, { rewritten, skipped: skipped.length, errors: errors.length });
|
|
122
131
|
(0, manifestManager_1.attemptManifestUpdate)();
|
|
123
132
|
}
|
|
124
133
|
return resp;
|
|
@@ -127,29 +136,98 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
127
136
|
const st = (0, indexContext_1.ensureLoaded)();
|
|
128
137
|
const toFix = [];
|
|
129
138
|
for (const e of st.list) {
|
|
130
|
-
const actual =
|
|
139
|
+
const actual = (0, instructions_shared_1.computeSourceHash)(e.body);
|
|
131
140
|
if (actual !== e.sourceHash)
|
|
132
141
|
toFix.push({ entry: e, actual });
|
|
133
142
|
}
|
|
134
|
-
if (!toFix.length)
|
|
135
|
-
return { repaired: 0, updated: [] };
|
|
136
143
|
const repaired = [];
|
|
144
|
+
const errors = [];
|
|
137
145
|
for (const { entry, actual } of toFix) {
|
|
138
146
|
try {
|
|
139
147
|
const updated = { ...entry, sourceHash: actual, updatedAt: new Date().toISOString() };
|
|
140
148
|
(0, indexContext_1.writeEntry)(updated);
|
|
141
149
|
repaired.push(entry.id);
|
|
142
150
|
}
|
|
143
|
-
catch {
|
|
151
|
+
catch (err) {
|
|
152
|
+
const detail = err.message || 'unknown';
|
|
153
|
+
errors.push({ id: entry.id, error: detail });
|
|
154
|
+
(0, auditLog_1.logAudit)('repair_entry_error', entry.id, { error: detail });
|
|
155
|
+
}
|
|
144
156
|
}
|
|
145
|
-
|
|
157
|
+
// Repair skipped files: scan disk for .json files not in the loaded index (#207)
|
|
158
|
+
const skippedRepaired = [];
|
|
159
|
+
const skippedErrors = [];
|
|
160
|
+
try {
|
|
161
|
+
const dir = (0, indexContext_1.getInstructionsDir)();
|
|
162
|
+
const diskFiles = fs_1.default.readdirSync(dir).filter(f => f.endsWith('.json') && !f.startsWith('_'));
|
|
163
|
+
// Use the compiled-in schema property set so disk-resident vs static-import
|
|
164
|
+
// schemas can never diverge (review #211 finding 9).
|
|
165
|
+
const schemaProps = (0, loaderSchemaValidator_1.getSchemaPropertyNames)();
|
|
166
|
+
for (const file of diskFiles) {
|
|
167
|
+
const id = file.replace(/\.json$/, '');
|
|
168
|
+
if (st.byId.has(id))
|
|
169
|
+
continue; // already loaded, not skipped
|
|
170
|
+
const filePath = path_1.default.join(dir, file);
|
|
171
|
+
try {
|
|
172
|
+
const raw = JSON.parse(fs_1.default.readFileSync(filePath, 'utf8'));
|
|
173
|
+
if (!raw.id || !raw.body) {
|
|
174
|
+
skippedErrors.push({ id, error: 'missing required fields (id or body)' });
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
// Strip properties not in the JSON schema
|
|
178
|
+
if (schemaProps.size) {
|
|
179
|
+
const keys = Object.keys(raw);
|
|
180
|
+
let stripped = false;
|
|
181
|
+
for (const key of keys) {
|
|
182
|
+
if (!schemaProps.has(key)) {
|
|
183
|
+
delete raw[key];
|
|
184
|
+
stripped = true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (stripped) {
|
|
188
|
+
raw.sourceHash = (0, instructions_shared_1.computeSourceHash)(String(raw.body));
|
|
189
|
+
raw.updatedAt = new Date().toISOString();
|
|
190
|
+
// DI-4: mirror the loader's migration step before validating
|
|
191
|
+
// against the loader schema, matching writeEntry/writeEntryAsync.
|
|
192
|
+
// Without this, a stripped record carrying a legacy schemaVersion
|
|
193
|
+
// would fail validateForDisk even though the loader would have
|
|
194
|
+
// migrated it transparently on read.
|
|
195
|
+
(0, schemaVersion_1.migrateInstructionRecord)(raw);
|
|
196
|
+
// Gate the write through the same loader-schema validator the
|
|
197
|
+
// primary writeEntry path uses (review #211 finding 3). If the
|
|
198
|
+
// stripped record still fails validation, skip the write rather
|
|
199
|
+
// than persisting a record that the next reload will silently
|
|
200
|
+
// discard.
|
|
201
|
+
const diskCheck = (0, loaderSchemaValidator_1.validateForDisk)(raw);
|
|
202
|
+
if (!diskCheck.valid) {
|
|
203
|
+
skippedErrors.push({ id, error: (0, instructionRecordValidation_1.sanitizeErrorDetail)(`schema validation failed: ${(diskCheck.errors || []).join('; ')}`) || 'schema validation failed' });
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
fs_1.default.writeFileSync(filePath, JSON.stringify(raw, null, 2) + '\n', 'utf8');
|
|
207
|
+
skippedRepaired.push(id);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (err) {
|
|
212
|
+
// Sanitize the raw error before returning to the client (review #211 finding 6).
|
|
213
|
+
skippedErrors.push({ id, error: (0, instructionRecordValidation_1.sanitizeErrorDetail)(err.message) || 'unknown' });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (err) {
|
|
218
|
+
errors.push({ id: '__disk_scan__', error: (0, instructionRecordValidation_1.sanitizeErrorDetail)(`skipped-file scan failed: ${err.message || 'unknown'}`) || 'skipped-file scan failed' });
|
|
219
|
+
}
|
|
220
|
+
const allRepaired = [...repaired, ...skippedRepaired];
|
|
221
|
+
if (allRepaired.length) {
|
|
146
222
|
(0, indexContext_1.touchIndexVersion)();
|
|
147
223
|
(0, indexContext_1.invalidate)();
|
|
148
224
|
(0, indexContext_1.ensureLoaded)();
|
|
149
225
|
}
|
|
150
|
-
const resp = {
|
|
151
|
-
|
|
152
|
-
|
|
226
|
+
const resp = {
|
|
227
|
+
repaired: allRepaired.length, updated: repaired, skippedRepaired, errors: [...errors, ...skippedErrors]
|
|
228
|
+
};
|
|
229
|
+
if (allRepaired.length) {
|
|
230
|
+
(0, auditLog_1.logAudit)('repair', allRepaired, { repaired: allRepaired.length, skippedRepaired: skippedRepaired.length, errors: errors.length + skippedErrors.length });
|
|
153
231
|
(0, manifestManager_1.attemptManifestUpdate)();
|
|
154
232
|
}
|
|
155
233
|
return resp;
|
|
@@ -222,12 +300,8 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
222
300
|
}
|
|
223
301
|
}
|
|
224
302
|
}
|
|
225
|
-
const isJunkCategory = (cat) => /^\d/.test(cat) || cat.length <= 1 || /^case-\d{6,}$/.test(cat);
|
|
226
303
|
for (const e of byId.values()) {
|
|
227
|
-
|
|
228
|
-
normCats = normCats.filter(c => !isJunkCategory(c));
|
|
229
|
-
normCats = normCats.filter(cat => !(cat.endsWith('s') && normCats.includes(cat.slice(0, -1))));
|
|
230
|
-
normCats = normCats.sort();
|
|
304
|
+
const normCats = (0, instructions_shared_1.normalizeCategories)(e.categories || []);
|
|
231
305
|
if (JSON.stringify(normCats) !== JSON.stringify(e.categories)) {
|
|
232
306
|
e.categories = normCats;
|
|
233
307
|
normalizedCategories++;
|
|
@@ -239,7 +313,7 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
239
313
|
if (mergeDuplicates) {
|
|
240
314
|
const groups = new Map();
|
|
241
315
|
for (const e of byId.values()) {
|
|
242
|
-
const key = e.sourceHash ||
|
|
316
|
+
const key = e.sourceHash || (0, instructions_shared_1.computeSourceHash)(e.body);
|
|
243
317
|
const arr = groups.get(key) || [];
|
|
244
318
|
arr.push(e);
|
|
245
319
|
groups.set(key, arr);
|
|
@@ -316,7 +390,9 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
316
390
|
}
|
|
317
391
|
}
|
|
318
392
|
}
|
|
319
|
-
catch {
|
|
393
|
+
catch (err) {
|
|
394
|
+
notes.push(`purgeLegacyScopes-failed:${e.id}:${err.message || String(err)}`);
|
|
395
|
+
}
|
|
320
396
|
}
|
|
321
397
|
if (dryRun && purgedScopes)
|
|
322
398
|
notes.push(`would-purge:${purgedScopes}`);
|
|
@@ -328,10 +404,12 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
328
404
|
const derived = (0, categoryRules_1.deriveCategory)(e.id);
|
|
329
405
|
if (derived === 'Other')
|
|
330
406
|
continue;
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
407
|
+
const slug = (0, categoryRules_1.slugifyCategory)(derived);
|
|
408
|
+
if (!slug)
|
|
409
|
+
continue;
|
|
410
|
+
e.primaryCategory = slug;
|
|
411
|
+
if (!e.categories.includes(slug)) {
|
|
412
|
+
e.categories = [...e.categories, slug].sort();
|
|
335
413
|
}
|
|
336
414
|
e.updatedAt = new Date().toISOString();
|
|
337
415
|
remappedCategories++;
|
|
@@ -341,7 +419,7 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
341
419
|
{
|
|
342
420
|
for (const e of byId.values()) {
|
|
343
421
|
const storedHash = e.sourceHash || '';
|
|
344
|
-
const actualHash =
|
|
422
|
+
const actualHash = (0, instructions_shared_1.computeSourceHash)(e.body);
|
|
345
423
|
if (storedHash !== actualHash) {
|
|
346
424
|
e.sourceHash = actualHash;
|
|
347
425
|
repairedHashes++;
|
|
@@ -351,6 +429,7 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
351
429
|
}
|
|
352
430
|
}
|
|
353
431
|
deprecatedRemoved = toRemove.length;
|
|
432
|
+
const errors = [];
|
|
354
433
|
if (!dryRun) {
|
|
355
434
|
for (const id of toRemove) {
|
|
356
435
|
byId.delete(id);
|
|
@@ -364,7 +443,10 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
364
443
|
filesRewritten++;
|
|
365
444
|
}
|
|
366
445
|
catch (err) {
|
|
367
|
-
|
|
446
|
+
const detail = err.message || String(err);
|
|
447
|
+
errors.push({ id, error: `write-failed: ${detail}` });
|
|
448
|
+
notes.push(`write-failed:${id}:${detail}`);
|
|
449
|
+
(0, auditLog_1.logAudit)('groom_entry_error', id, { error: detail, operation: 'write' });
|
|
368
450
|
}
|
|
369
451
|
}
|
|
370
452
|
for (const id of toRemove) {
|
|
@@ -372,7 +454,10 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
372
454
|
(0, indexContext_1.removeEntry)(id);
|
|
373
455
|
}
|
|
374
456
|
catch (err) {
|
|
375
|
-
|
|
457
|
+
const detail = err.message || String(err);
|
|
458
|
+
errors.push({ id, error: `delete-failed: ${detail}` });
|
|
459
|
+
notes.push(`delete-failed:${id}:${detail}`);
|
|
460
|
+
(0, auditLog_1.logAudit)('groom_entry_error', id, { error: detail, operation: 'delete' });
|
|
376
461
|
}
|
|
377
462
|
}
|
|
378
463
|
if (updated.size || toRemove.length) {
|
|
@@ -389,8 +474,10 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
389
474
|
}
|
|
390
475
|
const stAfter = (0, indexContext_1.ensureLoaded)();
|
|
391
476
|
const resp = { previousHash, hash: stAfter.hash, scanned, repairedHashes, normalizedCategories, deprecatedRemoved, duplicatesMerged, signalApplied, filesRewritten, purgedScopes, migrated, remappedCategories, dryRun, notes };
|
|
477
|
+
if (errors.length)
|
|
478
|
+
resp.errors = errors;
|
|
392
479
|
if (!dryRun && (repairedHashes || normalizedCategories || deprecatedRemoved || duplicatesMerged || signalApplied || filesRewritten || purgedScopes || migrated || remappedCategories)) {
|
|
393
|
-
(0, auditLog_1.logAudit)('groom', undefined, { repairedHashes, normalizedCategories, deprecatedRemoved, duplicatesMerged, signalApplied, filesRewritten, purgedScopes, migrated, remappedCategories });
|
|
480
|
+
(0, auditLog_1.logAudit)('groom', undefined, { repairedHashes, normalizedCategories, deprecatedRemoved, duplicatesMerged, signalApplied, filesRewritten, purgedScopes, migrated, remappedCategories, errors: errors.length });
|
|
394
481
|
(0, manifestManager_1.attemptManifestUpdate)();
|
|
395
482
|
}
|
|
396
483
|
return resp;
|
|
@@ -409,6 +496,7 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
409
496
|
} });
|
|
410
497
|
let scanned = 0, changed = 0, fixedHash = 0, fixedVersion = 0, fixedTier = 0, addedTimestamps = 0, addedContentType = 0;
|
|
411
498
|
const updatedIds = [];
|
|
499
|
+
const errors = [];
|
|
412
500
|
const scannedIds = new Set();
|
|
413
501
|
const SEMVER = /^\d+\.\d+\.\d+(?:[-+].*)?$/;
|
|
414
502
|
for (const dir of dirs) {
|
|
@@ -486,7 +574,8 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
486
574
|
try {
|
|
487
575
|
(0, indexContext_1.writeEntry)(rec);
|
|
488
576
|
}
|
|
489
|
-
catch {
|
|
577
|
+
catch (err) {
|
|
578
|
+
errors.push({ id: path_1.default.basename(full, '.json'), error: `write-failed: ${err.message || String(err)}` });
|
|
490
579
|
continue;
|
|
491
580
|
}
|
|
492
581
|
}
|
|
@@ -548,7 +637,8 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
548
637
|
try {
|
|
549
638
|
(0, indexContext_1.writeEntry)(rec);
|
|
550
639
|
}
|
|
551
|
-
catch {
|
|
640
|
+
catch (err) {
|
|
641
|
+
errors.push({ id: entry.id, error: `write-failed: ${err.message || String(err)}` });
|
|
552
642
|
continue;
|
|
553
643
|
}
|
|
554
644
|
}
|
|
@@ -563,13 +653,20 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
563
653
|
(0, indexContext_1.invalidate)();
|
|
564
654
|
(0, indexContext_1.ensureLoaded)();
|
|
565
655
|
}
|
|
566
|
-
catch {
|
|
656
|
+
catch (err) {
|
|
657
|
+
(0, auditLog_1.logAudit)('normalize_reload_error', undefined, { error: err.message });
|
|
658
|
+
}
|
|
567
659
|
try {
|
|
568
660
|
(0, manifestManager_1.attemptManifestUpdate)();
|
|
569
661
|
}
|
|
570
|
-
catch {
|
|
662
|
+
catch (err) {
|
|
663
|
+
(0, auditLog_1.logAudit)('normalize_manifest_error', undefined, { error: err.message });
|
|
664
|
+
}
|
|
571
665
|
}
|
|
572
|
-
|
|
666
|
+
const normalizeResp = { scanned, changed, fixedHash, fixedVersion, fixedTier, addedTimestamps, addedContentType, dryRun, updated: updatedIds };
|
|
667
|
+
if (errors.length)
|
|
668
|
+
normalizeResp.errors = errors;
|
|
669
|
+
return normalizeResp;
|
|
573
670
|
}));
|
|
574
671
|
// usage_flush (mutation)
|
|
575
672
|
(0, registry_1.registerHandler)('usage_flush', (0, instructions_shared_1.guard)('usage_flush', () => ({ flushed: true })));
|
|
@@ -12,6 +12,8 @@ const features_1 = require("../features");
|
|
|
12
12
|
const schemaVersion_1 = require("../../versioning/schemaVersion");
|
|
13
13
|
const classificationService_1 = require("../classificationService");
|
|
14
14
|
const ownershipService_1 = require("../ownershipService");
|
|
15
|
+
const instructionRecordValidation_1 = require("../instructionRecordValidation");
|
|
16
|
+
const instructionRecordValidation_2 = require("../instructionRecordValidation");
|
|
15
17
|
const auditLog_1 = require("../auditLog");
|
|
16
18
|
const runtimeConfig_1 = require("../../config/runtimeConfig");
|
|
17
19
|
const manifestManager_1 = require("../manifestManager");
|
|
@@ -39,7 +41,7 @@ function parseInlineEntries(rawEntries) {
|
|
|
39
41
|
return { error: { error: 'entries JSON parse error', detail: e.message } };
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
|
-
(0, registry_1.registerHandler)('index_import', (0, instructions_shared_1.guard)('index_import', (p) => {
|
|
44
|
+
(0, registry_1.registerHandler)('index_import', (0, instructions_shared_1.guard)('index_import', async (p) => {
|
|
43
45
|
let entries;
|
|
44
46
|
const mode = p.mode || 'skip';
|
|
45
47
|
if (Array.isArray(p.entries)) {
|
|
@@ -108,10 +110,21 @@ function parseInlineEntries(rawEntries) {
|
|
|
108
110
|
let imported = 0, skipped = 0, overwritten = 0;
|
|
109
111
|
const errors = [];
|
|
110
112
|
const classifier = new classificationService_1.ClassificationService();
|
|
113
|
+
const skippedIds = new Set();
|
|
114
|
+
const formatImportValidationError = (validationErrors) => `invalid_instruction: ${validationErrors.join('; ')}`;
|
|
111
115
|
for (const e of entries) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
const id = e?.id || 'unknown';
|
|
117
|
+
const requiredFieldErrors = [
|
|
118
|
+
!e?.id ? 'id: missing required field' : undefined,
|
|
119
|
+
e?.title === undefined ? 'title: missing required field' : undefined,
|
|
120
|
+
e?.body === undefined ? 'body: missing required field' : undefined,
|
|
121
|
+
e?.priority === undefined ? 'priority: missing required field' : undefined,
|
|
122
|
+
e?.audience === undefined ? 'audience: missing required field' : undefined,
|
|
123
|
+
e?.requirement === undefined ? 'requirement: missing required field' : undefined,
|
|
124
|
+
].filter((issue) => !!issue);
|
|
125
|
+
const surfaceValidation = e ? (0, instructionRecordValidation_1.validateInstructionInputSurface)(e) : { validationErrors: [], hints: [], schemaRef: 'index_add#input' };
|
|
126
|
+
if (!e || requiredFieldErrors.length || surfaceValidation.validationErrors.length) {
|
|
127
|
+
errors.push({ id, error: formatImportValidationError([...requiredFieldErrors, ...surfaceValidation.validationErrors]) });
|
|
115
128
|
continue;
|
|
116
129
|
}
|
|
117
130
|
const bodyTrimmed = typeof e.body === 'string' ? e.body.trim() : String(e.body);
|
|
@@ -121,11 +134,11 @@ function parseInlineEntries(rawEntries) {
|
|
|
121
134
|
continue;
|
|
122
135
|
}
|
|
123
136
|
const file = path_1.default.join(dir, `${e.id}.json`);
|
|
124
|
-
const stImport = (0, indexContext_1.
|
|
137
|
+
const stImport = await (0, indexContext_1.ensureLoadedAsync)();
|
|
125
138
|
const storeHas = stImport.byId.has(e.id);
|
|
126
139
|
const fileExists = storeHas || fs_1.default.existsSync(file);
|
|
127
140
|
const now = new Date().toISOString();
|
|
128
|
-
let categories =
|
|
141
|
+
let categories = (0, instructions_shared_1.normalizeInputCategories)(e.categories);
|
|
129
142
|
const primaryCategoryRaw = e.primaryCategory;
|
|
130
143
|
if (!categories.length) {
|
|
131
144
|
if (instructionsCfg.requireCategory) {
|
|
@@ -163,20 +176,11 @@ function parseInlineEntries(rawEntries) {
|
|
|
163
176
|
}
|
|
164
177
|
if (fileExists && mode === 'skip') {
|
|
165
178
|
skipped++;
|
|
179
|
+
skippedIds.add(e.id);
|
|
166
180
|
continue;
|
|
167
181
|
}
|
|
168
|
-
if (fileExists && mode === 'overwrite')
|
|
169
|
-
overwritten++;
|
|
170
|
-
else if (!fileExists)
|
|
171
|
-
imported++;
|
|
172
182
|
const base = existing ? { ...existing, title: e.title, body: bodyTrimmed, rationale: e.rationale, priority: e.priority, audience: e.audience, requirement: e.requirement, categories, primaryCategory: effectivePrimary, updatedAt: now } : { id: e.id, title: e.title, body: bodyTrimmed, rationale: e.rationale, priority: e.priority, audience: e.audience, requirement: e.requirement, categories, primaryCategory: effectivePrimary, sourceHash: newBodyHash, schemaVersion: schemaVersion_1.SCHEMA_VERSION, deprecatedBy: e.deprecatedBy, createdAt: now, updatedAt: now, riskScore: e.riskScore, createdByAgent: instructionsCfg.agentId, sourceWorkspace: instructionsCfg.workspaceId, extensions: e.extensions };
|
|
173
|
-
|
|
174
|
-
for (const k of govKeys) {
|
|
175
|
-
const v = e[k];
|
|
176
|
-
if (v !== undefined) {
|
|
177
|
-
base[k] = v;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
183
|
+
(0, instructions_shared_1.applyGovernanceKeys)(base, e, instructions_shared_1.IMPORT_GOVERNANCE_KEYS);
|
|
180
184
|
if (!base.sourceWorkspace)
|
|
181
185
|
base.sourceWorkspace = instructionsCfg.workspaceId;
|
|
182
186
|
base.sourceHash = newBodyHash;
|
|
@@ -188,18 +192,47 @@ function parseInlineEntries(rawEntries) {
|
|
|
188
192
|
record.updatedAt = new Date().toISOString();
|
|
189
193
|
}
|
|
190
194
|
}
|
|
195
|
+
const recordValidation = (0, instructionRecordValidation_1.validateInstructionRecord)(record);
|
|
196
|
+
if (recordValidation.validationErrors.length) {
|
|
197
|
+
errors.push({ id: e.id, error: formatImportValidationError(recordValidation.validationErrors) });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
191
200
|
try {
|
|
192
|
-
(0, indexContext_1.
|
|
201
|
+
await (0, indexContext_1.writeEntryAsync)(record);
|
|
193
202
|
}
|
|
194
|
-
catch {
|
|
195
|
-
|
|
203
|
+
catch (err) {
|
|
204
|
+
if ((0, instructionRecordValidation_2.isInstructionValidationError)(err)) {
|
|
205
|
+
errors.push({ id: e.id, error: formatImportValidationError(err.validationErrors) });
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
errors.push({ id: e.id, error: `write-failed: ${err.message || 'unknown'}` });
|
|
209
|
+
continue;
|
|
196
210
|
}
|
|
211
|
+
if (fileExists && mode === 'overwrite')
|
|
212
|
+
overwritten++;
|
|
213
|
+
else if (!fileExists)
|
|
214
|
+
imported++;
|
|
197
215
|
}
|
|
198
216
|
(0, indexContext_1.touchIndexVersion)();
|
|
199
217
|
(0, indexContext_1.invalidate)();
|
|
200
|
-
const st = (0, indexContext_1.
|
|
201
|
-
|
|
202
|
-
|
|
218
|
+
const st = await (0, indexContext_1.ensureLoadedAsync)();
|
|
219
|
+
// Read-back verification: confirm each written entry is visible in the reloaded index
|
|
220
|
+
const verificationErrors = [];
|
|
221
|
+
const writtenIds = entries
|
|
222
|
+
.filter(e => e.id && !errors.some(err => err.id === e.id) && !skippedIds.has(e.id))
|
|
223
|
+
.map(e => e.id);
|
|
224
|
+
for (const id of writtenIds) {
|
|
225
|
+
if (!st.byId.has(id)) {
|
|
226
|
+
verificationErrors.push({ id, error: 'not-in-index-after-reload' });
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (verificationErrors.length) {
|
|
230
|
+
errors.push(...verificationErrors);
|
|
231
|
+
(0, auditLog_1.logAudit)('import_verification', verificationErrors.map(v => v.id), { missingAfterReload: verificationErrors.length });
|
|
232
|
+
}
|
|
233
|
+
const verifiedCount = writtenIds.length - verificationErrors.length;
|
|
234
|
+
const summary = { hash: st.hash, imported, skipped, overwritten, total: entries.length, errors, verified: verificationErrors.length === 0, verifiedCount, verificationErrorCount: verificationErrors.length };
|
|
235
|
+
(0, auditLog_1.logAudit)('import', entries.map(e => e.id), { imported, skipped, overwritten, errors: errors.length, verified: verificationErrors.length === 0 });
|
|
203
236
|
(0, manifestManager_1.attemptManifestUpdate)();
|
|
204
237
|
return summary;
|
|
205
238
|
}));
|
|
@@ -7,40 +7,58 @@ const manifestManager_1 = require("../manifestManager");
|
|
|
7
7
|
const features_1 = require("../features");
|
|
8
8
|
const instructions_shared_1 = require("./instructions.shared");
|
|
9
9
|
(0, registry_1.registerHandler)('index_governanceHash', () => {
|
|
10
|
+
const reloadFailures = [];
|
|
11
|
+
let reloadSucceeded = false;
|
|
12
|
+
const captureReloadFailure = (stage, error) => {
|
|
13
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
14
|
+
reloadFailures.push(`${stage}: ${reason}`);
|
|
15
|
+
};
|
|
16
|
+
const reloadState = (stage) => {
|
|
17
|
+
(0, indexContext_1.invalidate)();
|
|
18
|
+
try {
|
|
19
|
+
const loaded = (0, indexContext_1.ensureLoaded)();
|
|
20
|
+
reloadSucceeded = true;
|
|
21
|
+
return loaded;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
captureReloadFailure(stage, error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
10
28
|
let st = (0, indexContext_1.ensureLoaded)();
|
|
11
29
|
const now = Date.now();
|
|
12
30
|
const loadedAgo = now - new Date(st.loadedAt).getTime();
|
|
13
31
|
if (loadedAgo > 50) {
|
|
14
32
|
try {
|
|
15
33
|
// Force reload if stale — trust the store for freshness
|
|
16
|
-
|
|
17
|
-
st = (0, indexContext_1.ensureLoaded)();
|
|
34
|
+
st = reloadState('stale-check');
|
|
18
35
|
}
|
|
19
|
-
catch { /*
|
|
36
|
+
catch { /* handled below */ }
|
|
20
37
|
}
|
|
21
38
|
let projections = st.list.slice().sort((a, b) => a.id.localeCompare(b.id)).map(indexContext_1.projectGovernance);
|
|
22
39
|
try {
|
|
23
40
|
const storeCount = st.list.length;
|
|
24
41
|
if (storeCount && (projections.length === 0 || projections.length < Math.floor(storeCount * 0.9))) {
|
|
25
42
|
// Late materialization: reload from store to pick up any missing entries
|
|
26
|
-
|
|
27
|
-
st = (0, indexContext_1.ensureLoaded)();
|
|
43
|
+
st = reloadState('late-materialization');
|
|
28
44
|
projections = st.list.slice().sort((a, b) => a.id.localeCompare(b.id)).map(indexContext_1.projectGovernance);
|
|
29
45
|
}
|
|
30
46
|
}
|
|
31
|
-
catch { /*
|
|
47
|
+
catch { /* handled below */ }
|
|
32
48
|
const governanceHash = (0, indexContext_1.computeGovernanceHash)(st.list);
|
|
33
49
|
if (projections.length && projections.length < Math.floor(st.list.length * 0.9) || projections.some(p => !p.owner)) {
|
|
34
50
|
try {
|
|
35
|
-
|
|
36
|
-
const st2 = (0, indexContext_1.ensureLoaded)();
|
|
51
|
+
const st2 = reloadState('projection-repair');
|
|
37
52
|
projections = st2.list.slice().sort((a, b) => a.id.localeCompare(b.id)).map(indexContext_1.projectGovernance);
|
|
38
53
|
try {
|
|
39
54
|
(0, features_1.incrementCounter)('governance:projectionRepair');
|
|
40
55
|
}
|
|
41
|
-
catch { /*
|
|
56
|
+
catch { /* counter-only — non-critical */ }
|
|
42
57
|
}
|
|
43
|
-
catch { /*
|
|
58
|
+
catch { /* handled below */ }
|
|
59
|
+
}
|
|
60
|
+
if (reloadFailures.length && !reloadSucceeded) {
|
|
61
|
+
throw new Error(`index_governanceHash could not refresh index state: ${reloadFailures.join('; ')}`);
|
|
44
62
|
}
|
|
45
63
|
return { count: projections.length, governanceHash, items: projections };
|
|
46
64
|
});
|
|
@@ -48,8 +66,10 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
48
66
|
const id = p.id;
|
|
49
67
|
const st = (0, indexContext_1.ensureLoaded)();
|
|
50
68
|
const existing = st.byId.get(id);
|
|
51
|
-
if (!existing)
|
|
69
|
+
if (!existing) {
|
|
70
|
+
(0, auditLog_1.logAudit)('governanceUpdate', id, { changed: false, notFound: true });
|
|
52
71
|
return { id, notFound: true };
|
|
72
|
+
}
|
|
53
73
|
// Read from store (in-memory), fall back to disk for JSON backend
|
|
54
74
|
const record = { ...existing };
|
|
55
75
|
let changed = false;
|
|
@@ -63,6 +83,7 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
63
83
|
const allowed = ['draft', 'review', 'approved', 'deprecated'];
|
|
64
84
|
const desired = p.status === 'active' ? 'approved' : p.status;
|
|
65
85
|
if (!allowed.includes(desired)) {
|
|
86
|
+
(0, auditLog_1.logAudit)('governanceUpdate', id, { changed: false, error: 'invalid status', provided: p.status });
|
|
66
87
|
return { id, error: 'invalid status', provided: p.status };
|
|
67
88
|
}
|
|
68
89
|
if (desired !== record.status) {
|
|
@@ -79,26 +100,10 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
79
100
|
changed = true;
|
|
80
101
|
}
|
|
81
102
|
if (bump && bump !== 'none') {
|
|
82
|
-
const
|
|
83
|
-
while (parts.length < 3)
|
|
84
|
-
parts.push(0);
|
|
85
|
-
if (bump === 'major')
|
|
86
|
-
parts[0]++;
|
|
87
|
-
else if (bump === 'minor')
|
|
88
|
-
parts[1]++;
|
|
89
|
-
else if (bump === 'patch')
|
|
90
|
-
parts[2]++;
|
|
91
|
-
if (bump === 'major') {
|
|
92
|
-
parts[1] = 0;
|
|
93
|
-
parts[2] = 0;
|
|
94
|
-
}
|
|
95
|
-
if (bump === 'minor') {
|
|
96
|
-
parts[2] = 0;
|
|
97
|
-
}
|
|
98
|
-
const newVersion = parts.join('.');
|
|
103
|
+
const newVersion = (0, instructions_shared_1.bumpVersion)(record.version, bump);
|
|
99
104
|
if (newVersion !== record.version) {
|
|
100
105
|
record.version = newVersion;
|
|
101
|
-
record.changeLog = [...(record.changeLog || []),
|
|
106
|
+
record.changeLog = [...(record.changeLog || []), (0, instructions_shared_1.createChangeLogEntry)(newVersion, `manual ${bump} bump via governanceUpdate`)];
|
|
102
107
|
changed = true;
|
|
103
108
|
}
|
|
104
109
|
}
|
|
@@ -108,14 +113,20 @@ const instructions_shared_1 = require("./instructions.shared");
|
|
|
108
113
|
try {
|
|
109
114
|
(0, indexContext_1.writeEntry)(record);
|
|
110
115
|
}
|
|
111
|
-
catch {
|
|
112
|
-
|
|
116
|
+
catch (err) {
|
|
117
|
+
const detail = err.message || 'unknown';
|
|
118
|
+
const errorType = err instanceof Error ? err.constructor.name : typeof err;
|
|
119
|
+
const stack = err instanceof Error ? err.stack?.slice(0, 500) : undefined;
|
|
120
|
+
// #132: full detail (including paths) only goes to the audit log; client gets a path-redacted version
|
|
121
|
+
const safeDetail = detail.replace(/[A-Za-z]:\\[^\s'"`]+/g, '<redacted-path>').replace(/\/(?:[^\s/'"`]+\/)+[^\s/'"`]+/g, '<redacted-path>');
|
|
122
|
+
(0, auditLog_1.logAudit)('governanceUpdate', id, { changed: false, error: detail, errorType, stack, writeFailure: true });
|
|
123
|
+
return { id, error: 'write-failed', detail: safeDetail, errorType };
|
|
113
124
|
}
|
|
114
125
|
(0, indexContext_1.touchIndexVersion)();
|
|
115
126
|
(0, indexContext_1.invalidate)();
|
|
116
127
|
(0, indexContext_1.ensureLoaded)();
|
|
117
128
|
const resp = { id, changed: true, version: record.version, owner: record.owner, status: record.status, lastReviewedAt: record.lastReviewedAt, nextReviewDue: record.nextReviewDue };
|
|
118
|
-
(0, auditLog_1.logAudit)('governanceUpdate', id, { changed: true, version: record.version });
|
|
129
|
+
(0, auditLog_1.logAudit)('governanceUpdate', id, { changed: true, version: record.version, owner: record.owner, status: record.status, lastReviewedAt: record.lastReviewedAt, nextReviewDue: record.nextReviewDue });
|
|
119
130
|
(0, manifestManager_1.attemptManifestUpdate)();
|
|
120
131
|
return resp;
|
|
121
132
|
}));
|