kibi-cli 0.12.5 → 0.12.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAiFA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAI1B,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA2bD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0V/B"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAiFA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAI1B,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA8bD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0W/B"}
@@ -98,6 +98,9 @@ function buildManifestLookup(stagedFiles) {
98
98
  manifestResults.push({
99
99
  entity: entry.entity,
100
100
  relationships: entry.relationships,
101
+ ...(entry.sourceFile !== undefined
102
+ ? { sourceFile: entry.sourceFile }
103
+ : {}),
101
104
  });
102
105
  const authoredSymbolResult = {
103
106
  entity: entry.entity,
@@ -440,8 +443,11 @@ export async function checkCommand(options) {
440
443
  }
441
444
  }
442
445
  const markdownResults = markdownFiles.map((file) => extractFromMarkdownString(file.content ?? "", file.path));
446
+ const stagedSourceFilePaths = new Set(sourceFiles.map((file) => file.path));
447
+ const scopedManifestResults = manifestResults.filter((result) => result.sourceFile !== undefined &&
448
+ stagedSourceFilePaths.has(result.sourceFile));
443
449
  const stagedEntityResults = [
444
- ...manifestResults,
450
+ ...scopedManifestResults,
445
451
  ...markdownResults,
446
452
  ];
447
453
  const stagedKibiEvidence = buildStagedKibiImpactEvidence({
@@ -464,6 +470,13 @@ export async function checkCommand(options) {
464
470
  sourceContentByFile,
465
471
  }));
466
472
  if (allSymbols.length === 0 && stagedEntityResults.length === 0) {
473
+ if (stagedKibiDiagnostics.length > 0) {
474
+ console.log(formatStagedKibiDiagnostics(stagedKibiDiagnostics));
475
+ if (options.dryRun) {
476
+ return { exitCode: 0 };
477
+ }
478
+ return { exitCode: 1 };
479
+ }
467
480
  console.log("No exported symbols or staged entities found in staged files.");
468
481
  return { exitCode: 0 };
469
482
  }
@@ -1 +1 @@
1
- {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAqCA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAiWD,wBAAsB,cAAc,CAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgI/B"}
1
+ {"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"AAqCA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA8WD,wBAAsB,cAAc,CAClC,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+I/B"}
@@ -110,12 +110,20 @@ function buildMigrationAuditRecord(args) {
110
110
  configPath: args.configPath,
111
111
  fromVersion: args.fromVersion,
112
112
  migratedAt: args.migratedAt,
113
+ semanticAdvisorBackfill: args.semanticAdvisorBackfill,
113
114
  status: "applied",
114
115
  symbolGranularityLegacyLinks: args.symbolGranularityLegacyLinks,
115
116
  toVersion: LATEST_KB_SCHEMA_VERSION,
116
117
  warning: args.warning,
117
118
  };
118
119
  }
120
+ function normalizeSemanticAdvisorBackfill(value) {
121
+ return value === "pending" ||
122
+ value === "completed" ||
123
+ value === "not_applicable"
124
+ ? value
125
+ : null;
126
+ }
119
127
  const TRACEABILITY_RELATIONSHIP_TYPES = new Set([
120
128
  "implements",
121
129
  "covered_by",
@@ -305,12 +313,17 @@ export async function migrateCommand(options = {}) {
305
313
  config,
306
314
  dryRun: options.dryRun || !options.yes,
307
315
  });
316
+ const semanticAdvisorBackfill = normalizeSemanticAdvisorBackfill(config.semanticAdvisorBackfill) ??
317
+ "pending";
308
318
  if (options.dryRun) {
309
319
  console.log(`dry run: would migrate ${configPathRelative} schemaVersion from ${fromVersionLabel} to ${LATEST_KB_SCHEMA_VERSION}.`);
310
320
  console.log(`dry run: would write migration audit metadata to ${auditPathRelative}.`);
311
321
  if (symbolGranularityMigration.count > 0) {
312
322
  console.log(`dry run: would mark ${symbolGranularityMigration.count} legacy coarse symbol link(s) in ${toRelativePath(cwd, symbolGranularityMigration.manifestPath ?? "symbols.yaml")}.`);
313
323
  }
324
+ if (semanticAdvisorBackfill === "pending") {
325
+ console.log(`dry run: would mark semantic advisor backfill as pending in ${configPathRelative}.`);
326
+ }
314
327
  console.log("Re-run with --yes to apply these changes.");
315
328
  return { exitCode: 0 };
316
329
  }
@@ -323,6 +336,7 @@ export async function migrateCommand(options = {}) {
323
336
  const nextConfig = {
324
337
  ...config,
325
338
  schemaVersion: LATEST_KB_SCHEMA_VERSION,
339
+ semanticAdvisorBackfill,
326
340
  };
327
341
  const migratedAt = new Date().toISOString();
328
342
  writeJsonAtomically(configPath, nextConfig);
@@ -331,6 +345,7 @@ export async function migrateCommand(options = {}) {
331
345
  configPath: configPathRelative,
332
346
  fromVersion: configStatus.currentVersion,
333
347
  migratedAt,
348
+ semanticAdvisorBackfill,
334
349
  symbolGranularityLegacyLinks: symbolGranularityMigration.count,
335
350
  warning: migrationWarning,
336
351
  }));
@@ -338,6 +353,9 @@ export async function migrateCommand(options = {}) {
338
353
  if (symbolGranularityMigration.count > 0) {
339
354
  console.log(`Marked ${symbolGranularityMigration.count} existing coarse symbol link(s) as legacy-link.`);
340
355
  }
356
+ if (semanticAdvisorBackfill === "pending") {
357
+ console.log(`Marked semantic advisor backfill as pending in ${configPathRelative}.`);
358
+ }
341
359
  console.log(`Wrote migration audit metadata to ${auditPathRelative}.`);
342
360
  console.log("Migration complete. Future 'kibi migrate' runs will be a no-op.");
343
361
  return { exitCode: 0 };
@@ -1 +1 @@
1
- {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/persistence.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA0ErD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,eAAe,CAEnC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAqEvD;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,kBAAkB,EAAE,qBAAqB,EAAE,GAC1C,OAAO,CAAC;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAiI7D"}
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/persistence.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA0ErD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAmCD,wBAAsB,eAAe,CAEnC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAqEvD;AAGD,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,kBAAkB,EAAE,qBAAqB,EAAE,GAC1C,OAAO,CAAC;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CA+K7D"}
@@ -80,6 +80,30 @@ function serializeTypedFactFields(entity) {
80
80
  }
81
81
  return fields;
82
82
  }
83
+ function isQueryFailedError(error) {
84
+ const lowered = error.toLowerCase();
85
+ // Only match session-corruption errors that might benefit from a Prolog process restart.
86
+ // Data errors (entity missing, invalid relationship) are deterministic and should NOT
87
+ // trigger a restart — the error would persist and the restart would lose kb_attach state.
88
+ return (lowered.includes("query failed") ||
89
+ lowered.includes("query returned false") ||
90
+ lowered.includes("predicate or file not found"));
91
+ }
92
+ async function tryResetPrologProcess(prolog) {
93
+ const maybe = prolog;
94
+ if (typeof maybe.terminate !== "function" ||
95
+ typeof maybe.start !== "function") {
96
+ return false;
97
+ }
98
+ try {
99
+ await maybe.terminate();
100
+ await maybe.start();
101
+ return true;
102
+ }
103
+ catch {
104
+ return false;
105
+ }
106
+ }
83
107
  export async function persistEntities(
84
108
  // implements REQ-009
85
109
  prolog, results, entityIds) {
@@ -146,6 +170,7 @@ prolog, results, entityIds) {
146
170
  }
147
171
  return { entityCount, kbModified };
148
172
  }
173
+ // implements REQ-core-persistence
149
174
  export async function persistRelationships(prolog, results, shardRelationships) {
150
175
  let relCount = 0;
151
176
  let kbModified = false;
@@ -216,6 +241,7 @@ export async function persistRelationships(prolog, results, shardRelationships)
216
241
  }
217
242
  // Retry failed relationships
218
243
  const retryCount = 3;
244
+ let resetAttempted = false;
219
245
  for (let pass = 0; pass < retryCount && failedRelationships.length > 0; pass++) {
220
246
  const remainingFailed = [];
221
247
  for (const { rel, fromId, toId } of failedRelationships) {
@@ -242,6 +268,17 @@ export async function persistRelationships(prolog, results, shardRelationships)
242
268
  }
243
269
  failedRelationships.length = 0;
244
270
  failedRelationships.push(...remainingFailed);
271
+ const allLookLikeSessionFailures = remainingFailed.length > 0 &&
272
+ remainingFailed.every(({ error }) => isQueryFailedError(error));
273
+ // If every pending relationship fails with generic query errors, attempt one
274
+ // best-effort Prolog restart before the next retry pass to recover from a
275
+ // potentially poisoned interactive session.
276
+ if (allLookLikeSessionFailures &&
277
+ !resetAttempted &&
278
+ pass + 1 < retryCount) {
279
+ resetAttempted = true;
280
+ await tryResetPrologProcess(prolog);
281
+ }
245
282
  }
246
283
  // Log remaining failures
247
284
  if (failedRelationships.length > 0) {
@@ -255,7 +292,28 @@ export async function persistRelationships(prolog, results, shardRelationships)
255
292
  console.warn(` Error: ${error}`);
256
293
  }
257
294
  }
258
- console.warn("\nTip: Ensure target entities exist before creating relationships.");
295
+ const missingEntities = failedRelationships.filter(({ error }) => error.toLowerCase().includes("entity does not exist"));
296
+ const invalidRels = failedRelationships.filter(({ error }) => error.includes("Invalid relationship"));
297
+ if (missingEntities.length > 0) {
298
+ // Collect unique missing entity IDs from error messages
299
+ const missingIds = new Set();
300
+ for (const { error } of missingEntities) {
301
+ const match = error.match(/does not exist: (.+)$/);
302
+ const missingId = match?.[1];
303
+ if (missingId !== undefined) {
304
+ missingIds.add(missingId.trim());
305
+ }
306
+ }
307
+ const idList = [...missingIds].sort().join(", ");
308
+ console.warn(`\nTip: ${missingEntities.length} relationship(s) reference ${missingIds.size} missing entity/ies: ${idList}.`);
309
+ console.warn(" Create the missing docs (e.g., docs/requirements/REQ-*.md) or remove stale relationships.");
310
+ }
311
+ else if (invalidRels.length > 0) {
312
+ console.warn("\nTip: Check that relationship types and directions match the allowed schema (e.g., implements symbol→req, verified_by req→test).");
313
+ }
314
+ else {
315
+ console.warn("\nTip: Ensure target entities exist before creating relationships.");
316
+ }
259
317
  }
260
318
  return { relationshipCount: relCount, kbModified };
261
319
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/extractors/manifest.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAc,SAAQ,KAAK;IAG7B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAK1B;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AA2HD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,gBAAgB,EAAE,CAmBpB;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,oBAAoB,EAAE,CAmBxB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAKxE;AAsCD,wBAAgB,iCAAiC,CAC/C,YAAY,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,EAAE,CAWxB"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/extractors/manifest.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA6JD,qBAAa,aAAc,SAAQ,KAAK;IAG7B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAK1B;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAqID,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,gBAAgB,EAAE,CAmBpB;AAED,wBAAgB,kCAAkC,CAChD,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,GACf,oBAAoB,EAAE,CAmBxB;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAKxE;AAsCD,wBAAgB,iCAAiC,CAC/C,YAAY,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACvB,oBAAoB,EAAE,CAWxB"}
@@ -21,6 +21,92 @@ import * as path from "node:path";
21
21
  import { load as parseYAML } from "js-yaml";
22
22
  import { DEFAULT_COORDINATES_PATH } from "../utils/manifest-paths.js";
23
23
  import { mergeCoordinatesWithManifest, readCoordinateArtifact, } from "./symbol-coordinates.js";
24
+ const VALID_RELATIONSHIP_TYPES = new Set([
25
+ "depends_on",
26
+ "executable_for",
27
+ "specified_by",
28
+ "verified_by",
29
+ "validates",
30
+ "implements",
31
+ "covered_by",
32
+ "constrained_by",
33
+ "constrains",
34
+ "requires_property",
35
+ "requires_predicate",
36
+ "guards",
37
+ "publishes",
38
+ "consumes",
39
+ "supersedes",
40
+ "relates_to",
41
+ ]);
42
+ const VALID_RELATIONSHIP_DIRECTIONS = [
43
+ { type: "depends_on", from: "req", to: "req" },
44
+ { type: "executable_for", from: "symbol", to: "test" },
45
+ { type: "specified_by", from: "req", to: "scenario" },
46
+ { type: "verified_by", from: "req", to: "test" },
47
+ { type: "verified_by", from: "scenario", to: "test" },
48
+ { type: "validates", from: "test", to: "req" },
49
+ { type: "validates", from: "test", to: "scenario" },
50
+ { type: "implements", from: "symbol", to: "req" },
51
+ { type: "covered_by", from: "symbol", to: "test" },
52
+ { type: "constrained_by", from: "symbol", to: "adr" },
53
+ { type: "constrains", from: "req", to: "fact" },
54
+ { type: "requires_property", from: "req", to: "fact" },
55
+ { type: "requires_predicate", from: "req", to: "fact" },
56
+ { type: "guards", from: "flag", to: "symbol" },
57
+ { type: "guards", from: "flag", to: "event" },
58
+ { type: "guards", from: "flag", to: "req" },
59
+ { type: "publishes", from: "symbol", to: "event" },
60
+ { type: "consumes", from: "symbol", to: "event" },
61
+ { type: "supersedes", from: "adr", to: "adr" },
62
+ { type: "supersedes", from: "req", to: "req" },
63
+ ];
64
+ const RELATIONSHIP_TYPE_DISPLAY_LIST = Array.from(VALID_RELATIONSHIP_TYPES)
65
+ .sort()
66
+ .join(", ");
67
+ function inferEntityTypeFromId(id) {
68
+ const upper = id.toUpperCase();
69
+ if (upper.startsWith("REQ-"))
70
+ return "req";
71
+ if (upper.startsWith("SCEN-"))
72
+ return "scenario";
73
+ if (upper.startsWith("TEST-"))
74
+ return "test";
75
+ if (upper.startsWith("ADR-"))
76
+ return "adr";
77
+ if (upper.startsWith("FLAG-"))
78
+ return "flag";
79
+ if (upper.startsWith("EVENT-"))
80
+ return "event";
81
+ if (upper.startsWith("SYM-"))
82
+ return "symbol";
83
+ if (upper.startsWith("FACT-"))
84
+ return "fact";
85
+ return null;
86
+ }
87
+ function validateRelationshipType(type, filePath) {
88
+ if (!VALID_RELATIONSHIP_TYPES.has(type)) {
89
+ throw new ManifestError(`Invalid relationship type \"${type}\". Allowed types: ${RELATIONSHIP_TYPE_DISPLAY_LIST}`, filePath);
90
+ }
91
+ }
92
+ function validateRelationshipDirection(type, from, to, filePath) {
93
+ if (type === "relates_to") {
94
+ return;
95
+ }
96
+ const fromType = inferEntityTypeFromId(from);
97
+ const toType = inferEntityTypeFromId(to);
98
+ // If we cannot infer one side from ID prefix, defer to downstream existence/type checks.
99
+ if (!fromType || !toType) {
100
+ return;
101
+ }
102
+ const valid = VALID_RELATIONSHIP_DIRECTIONS.some((rule) => rule.type === type && rule.from === fromType && rule.to === toType);
103
+ if (!valid) {
104
+ const allowed = VALID_RELATIONSHIP_DIRECTIONS.filter((rule) => rule.type === type)
105
+ .map((rule) => `${rule.from} -> ${rule.to}`)
106
+ .join(", ");
107
+ throw new ManifestError(`Invalid relationship direction for \"${type}\": ${fromType} -> ${toType}. Allowed: ${allowed}`, filePath);
108
+ }
109
+ }
24
110
  export class ManifestError extends Error {
25
111
  filePath;
26
112
  constructor(message, filePath) {
@@ -35,7 +121,7 @@ function getManifestSymbols(manifest, filePath) {
35
121
  }
36
122
  return manifest.symbols;
37
123
  }
38
- function extractRelationships(id, symbol) {
124
+ function extractRelationships(id, symbol, filePath) {
39
125
  const relationships = [];
40
126
  if (Array.isArray(symbol.links)) {
41
127
  for (const link of symbol.links) {
@@ -50,6 +136,8 @@ function extractRelationships(id, symbol) {
50
136
  const typedLink = link;
51
137
  if (typeof typedLink.type === "string" &&
52
138
  typeof typedLink.target === "string") {
139
+ validateRelationshipType(typedLink.type, filePath);
140
+ validateRelationshipDirection(typedLink.type, id, typedLink.target, filePath);
53
141
  relationships.push({
54
142
  type: typedLink.type,
55
143
  from: id,
@@ -64,6 +152,8 @@ function extractRelationships(id, symbol) {
64
152
  if (rel &&
65
153
  typeof rel.type === "string" &&
66
154
  typeof rel.target === "string") {
155
+ validateRelationshipType(rel.type, filePath);
156
+ validateRelationshipDirection(rel.type, id, rel.target, filePath);
67
157
  relationships.push({
68
158
  type: rel.type,
69
159
  from: id,
@@ -103,7 +193,7 @@ function extractFromManifestSymbolRecords(manifestSymbols, filePath) {
103
193
  const sourceFile = symbol.sourceFile ?? symbol.source;
104
194
  return {
105
195
  entity,
106
- relationships: extractRelationships(id, symbol),
196
+ relationships: extractRelationships(id, symbol, filePath),
107
197
  ...(sourceFile !== undefined ? { sourceFile } : {}),
108
198
  };
109
199
  });
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"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"}
@@ -58,6 +58,96 @@ const DEFAULT_STATUS_BY_TYPE = {
58
58
  symbol: "active",
59
59
  fact: "active",
60
60
  };
61
+ const VALID_RELATIONSHIP_TYPES = new Set([
62
+ "depends_on",
63
+ "executable_for",
64
+ "specified_by",
65
+ "verified_by",
66
+ "validates",
67
+ "implements",
68
+ "covered_by",
69
+ "constrained_by",
70
+ "constrains",
71
+ "requires_property",
72
+ "requires_predicate",
73
+ "guards",
74
+ "publishes",
75
+ "consumes",
76
+ "supersedes",
77
+ "relates_to",
78
+ ]);
79
+ const VALID_RELATIONSHIP_DIRECTIONS = [
80
+ { type: "depends_on", from: "req", to: "req" },
81
+ { type: "executable_for", from: "symbol", to: "test" },
82
+ { type: "specified_by", from: "req", to: "scenario" },
83
+ { type: "verified_by", from: "req", to: "test" },
84
+ { type: "verified_by", from: "scenario", to: "test" },
85
+ { type: "validates", from: "test", to: "req" },
86
+ { type: "validates", from: "test", to: "scenario" },
87
+ { type: "implements", from: "symbol", to: "req" },
88
+ { type: "covered_by", from: "symbol", to: "test" },
89
+ { type: "constrained_by", from: "symbol", to: "adr" },
90
+ { type: "constrains", from: "req", to: "fact" },
91
+ { type: "requires_property", from: "req", to: "fact" },
92
+ { type: "requires_predicate", from: "req", to: "fact" },
93
+ { type: "guards", from: "flag", to: "symbol" },
94
+ { type: "guards", from: "flag", to: "event" },
95
+ { type: "guards", from: "flag", to: "req" },
96
+ { type: "publishes", from: "symbol", to: "event" },
97
+ { type: "consumes", from: "symbol", to: "event" },
98
+ { type: "supersedes", from: "adr", to: "adr" },
99
+ { type: "supersedes", from: "req", to: "req" },
100
+ ];
101
+ const RELATIONSHIP_TYPE_DISPLAY_LIST = Array.from(VALID_RELATIONSHIP_TYPES)
102
+ .sort()
103
+ .join(", ");
104
+ function inferEntityTypeFromId(id) {
105
+ const upper = id.toUpperCase();
106
+ if (upper.startsWith("REQ-"))
107
+ return "req";
108
+ if (upper.startsWith("SCEN-"))
109
+ return "scenario";
110
+ if (upper.startsWith("TEST-"))
111
+ return "test";
112
+ if (upper.startsWith("ADR-"))
113
+ return "adr";
114
+ if (upper.startsWith("FLAG-"))
115
+ return "flag";
116
+ if (upper.startsWith("EVENT-"))
117
+ return "event";
118
+ if (upper.startsWith("SYM-"))
119
+ return "symbol";
120
+ if (upper.startsWith("FACT-"))
121
+ return "fact";
122
+ return null;
123
+ }
124
+ function validateRelationshipType(type, filePath) {
125
+ if (!VALID_RELATIONSHIP_TYPES.has(type)) {
126
+ throw new FrontmatterError(`Invalid relationship type \"${type}\". Allowed types: ${RELATIONSHIP_TYPE_DISPLAY_LIST}`, filePath, {
127
+ classification: "Invalid Relationship Type",
128
+ hint: "Use one of the supported relationship types from relationship.schema.json.",
129
+ });
130
+ }
131
+ }
132
+ function validateRelationshipDirection(type, fromType, toId, filePath) {
133
+ if (type === "relates_to") {
134
+ return;
135
+ }
136
+ const toType = inferEntityTypeFromId(toId);
137
+ if (!toType) {
138
+ return;
139
+ }
140
+ const valid = VALID_RELATIONSHIP_DIRECTIONS.some((rule) => rule.type === type && rule.from === fromType && rule.to === toType);
141
+ if (!valid) {
142
+ const allowed = VALID_RELATIONSHIP_DIRECTIONS.filter((rule) => rule.type === type)
143
+ .map((rule) => `${rule.from} -> ${rule.to}`)
144
+ .join(", ");
145
+ throw new FrontmatterError(`Invalid relationship direction for \"${type}\": ${fromType} -> ${toType}. Allowed: ${allowed}`, filePath, {
146
+ classification: "Invalid Relationship Direction",
147
+ hint: "Reverse the link direction or use the correct relationship type for these entity kinds.",
148
+ });
149
+ }
150
+ }
61
151
  export class FrontmatterError extends Error {
62
152
  filePath;
63
153
  classification;
@@ -224,6 +314,8 @@ function extractFromMarkdownContent(content, filePath) {
224
314
  typeof link === "object" &&
225
315
  typeof link.type === "string" &&
226
316
  typeof link.target === "string") {
317
+ validateRelationshipType(link.type, filePath);
318
+ validateRelationshipDirection(link.type, type, link.target, filePath);
227
319
  relationships.push({
228
320
  type: link.type,
229
321
  from: id,
@@ -1 +1 @@
1
- {"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/extractors/relationships.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAsBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,MAAM,GACvB,qBAAqB,EAAE,CAyCzB;AAmDD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,EAAE,GAC/B,qBAAqB,EAAE,CAEzB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,qBAAqB,EAAE,EACtC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,KAAK,CAAC;IACP,YAAY,EAAE,qBAAqB,CAAC;IACpC,KAAK,EAAE,cAAc,GAAG,YAAY,CAAC;CACtC,CAAC,CAgBD"}
1
+ {"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/extractors/relationships.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE;QACT,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAwBD;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,MAAM,GACvB,qBAAqB,EAAE,CAyCzB;AAmDD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,qBAAqB,EAAE,GAC/B,qBAAqB,EAAE,CAEzB;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,qBAAqB,EAAE,EACtC,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,KAAK,CAAC;IACP,YAAY,EAAE,qBAAqB,CAAC;IACpC,KAAK,EAAE,cAAc,GAAG,YAAY,CAAC;CACtC,CAAC,CAgBD"}
@@ -23,6 +23,7 @@ import { readShard } from "../relationships/shards.js";
23
23
  */
24
24
  const VALID_RELATIONSHIP_TYPES = new Set([
25
25
  "depends_on",
26
+ "executable_for",
26
27
  "specified_by",
27
28
  "verified_by",
28
29
  "validates",
@@ -31,6 +32,7 @@ const VALID_RELATIONSHIP_TYPES = new Set([
31
32
  "constrained_by",
32
33
  "constrains",
33
34
  "requires_property",
35
+ "requires_predicate",
34
36
  "guards",
35
37
  "publishes",
36
38
  "consumes",
@@ -1 +1 @@
1
- {"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,kBAAkB,CAA8B;IACxD,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAuCd,YAAY;IA0CpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAmK1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,kBAAkB;YAIZ,YAAY;IA2C1B,OAAO,CAAC,WAAW;IAsGnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,cAAc;IA+BtB,SAAS,IAAI,OAAO;IAMpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAwCjC"}
1
+ {"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,kBAAkB,CAA8B;IACxD,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAuCd,YAAY;IA2CpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAyK1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,kBAAkB;YAIZ,YAAY;IA2C1B,OAAO,CAAC,WAAW;IAsGnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,cAAc;IA6DtB,SAAS,IAAI,OAAO;IAMpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAwCjC"}
package/dist/prolog.js CHANGED
@@ -133,6 +133,7 @@ export class PrologProcess {
133
133
  this.outputBuffer = "";
134
134
  this.errorBuffer = "";
135
135
  }
136
+ // implements REQ-core-prolog-process-management
136
137
  async query(goal) {
137
138
  const isSingleGoal = typeof goal === "string";
138
139
  const goalKey = isSingleGoal ? goal : null;
@@ -165,8 +166,8 @@ export class PrologProcess {
165
166
  const debug = isPrologDebugEnabled();
166
167
  const normalizedGoal = this.normalizeGoal(goal);
167
168
  const wrappedGoal = /^once\s*\(/.test(normalizedGoal)
168
- ? normalizedGoal
169
- : `once((${normalizedGoal}))`;
169
+ ? `catch(${normalizedGoal}, _E, (print_message(error, _E), fail))`
170
+ : `catch(once((${normalizedGoal})), _E, (print_message(error, _E), fail))`;
170
171
  const start = Date.now();
171
172
  if (debug) {
172
173
  console.error(`[prolog debug] start query: ${normalizedGoal}`);
@@ -240,10 +241,15 @@ export class PrologProcess {
240
241
  if (debug) {
241
242
  console.error(`[prolog debug] query failed (false): ${normalizedGoal}`);
242
243
  }
244
+ // Check errorBuffer first — Prolog catch/3 writes ERROR to stderr
245
+ // then fail to stdout. If stderr has the real error, surface it.
246
+ const errorMessage = this.errorBuffer.length > 0 && this.errorBuffer.includes("ERROR")
247
+ ? this.translateError(this.errorBuffer)
248
+ : "Query failed";
243
249
  resolve({
244
250
  success: false,
245
251
  bindings: {},
246
- error: "Query failed",
252
+ error: errorMessage,
247
253
  });
248
254
  return;
249
255
  }
@@ -435,7 +441,35 @@ export class PrologProcess {
435
441
  }
436
442
  return bindings;
437
443
  }
444
+ // implements REQ-core-prolog-process-management
438
445
  translateError(errorText) {
446
+ // SWI-Prolog print_message/2 formats errors as human-readable messages,
447
+ // not raw Prolog terms. Match the actual output format.
448
+ if (errorText.includes("does not exist") &&
449
+ /entity [`'"].+?[`'"]/.test(errorText)) {
450
+ // SWI-Prolog doubles single quotes in formatted messages: ''REQ-TEST''
451
+ const entityIdMatch = errorText.match(/entity [`'"]+(.+?)[`'"]+ does not exist/);
452
+ const matchedEntityId = entityIdMatch?.[1];
453
+ const entityId = matchedEntityId !== undefined
454
+ ? matchedEntityId.replace(/^`?'+|'+`?$/g, "")
455
+ : "unknown";
456
+ if (errorText.includes("Target entity does not exist")) {
457
+ return `Target entity does not exist: ${entityId}`;
458
+ }
459
+ if (errorText.includes("Source entity does not exist")) {
460
+ return `Source entity does not exist: ${entityId}`;
461
+ }
462
+ return `Entity does not exist: ${entityId}`;
463
+ }
464
+ if (errorText.includes("Type error: `relationship' expected")) {
465
+ const relMatch = errorText.match(/\(Invalid relationship: ([^)]+)\)/);
466
+ const invalidRelationship = relMatch?.[1];
467
+ if (invalidRelationship !== undefined) {
468
+ return `Invalid relationship: ${invalidRelationship.trim()}`;
469
+ }
470
+ return "Invalid relationship type or direction";
471
+ }
472
+ // Fallback: check for raw Prolog error terms (used by other tools)
439
473
  if (errorText.includes("existence_error") ||
440
474
  errorText.includes("Unknown procedure")) {
441
475
  return "Predicate or file not found";
@@ -170,6 +170,12 @@ Anti-example: do not create a `flag` named `BUG-123` to track a defect. Create a
170
170
 
171
171
  Always confirm or create endpoint entities before linking them. Query target IDs with `kb_query` first. If an endpoint does not exist, create it with `kb_upsert` before creating the relationship. Creating relationships to non-existent entities produces dangling references that `kb_check` will flag.
172
172
 
173
+ For `kb_upsert`, keep relationship rows anchored to the entity you are upserting: each row's `from` must equal the upserted entity ID. If you need a `SYM -> REQ` link, upsert the symbol endpoint first, then link that symbol to the requirement.
174
+
175
+ Keep symbol payloads minimal: include only the fields needed to identify the symbol, its status, and its source traceability. Put extra prose, examples, or audit notes in docs or evidence files instead of custom `kb_upsert.properties`; strict `kb_upsert.properties` rejects unknown fields.
176
+
177
+ When a generic `Query failed` appears, do not keep retrying the same payload. First call `kb_validate_upsert`, query or create the missing endpoints, reduce the payload to the minimum required fields, and retry once. If it still fails, report the blocker instead of looping.
178
+
173
179
  ## Sequential Upserts
174
180
 
175
181
  Never fire `kb_upsert` calls in parallel. Execute them sequentially to avoid lock contention and ensure deterministic ordering. This is especially important when creating chains of related entities.
@@ -200,6 +206,7 @@ Call `kb_status` when you suspect the branch KB is stale or when switching conte
200
206
  | Missing `kb_check` | Undetected dangling refs and violations | Run targeted checks during work, full check at completion |
201
207
  | Tags as multi-ID lookup | Tags are metadata, not identifiers | Use `kb_query` with explicit `id` values |
202
208
  | `relates_to` for strict modeling | Loses contradiction safety | Use `constrains` and `requires_property` instead |
209
+ | `status: implemented` on requirements | Not a valid lifecycle status | Use a valid status such as `closed`, add an `implemented` tag, and link evidence instead |
203
210
 
204
211
  Before/after for reversed direction:
205
212
 
@@ -1 +1 @@
1
- {"version":3,"file":"temp-kb.d.ts","sourceRoot":"","sources":["../../src/traceability/temp-kb.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CASvC;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAE/B,OAAO,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,KAAK,aAAa,GACpD,IAAI,CAEN;AA4ID,iBAAe,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB/D;AAED,OAAO,EAAE,cAAc,EAAE,CAAC;AAG1B,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA6C7E;AAGD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAkBrE;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlE"}
1
+ {"version":3,"file":"temp-kb.d.ts","sourceRoot":"","sources":["../../src/traceability/temp-kb.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,gBAAgB,EACjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAMD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CASvC;AAOD;;;GAGG;AACH,wBAAgB,iBAAiB,CAE/B,OAAO,EAAE,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,KAAK,aAAa,GACpD,IAAI,CAEN;AA4ID,iBAAe,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB/D;AAED,OAAO,EAAE,cAAc,EAAE,CAAC;AAG1B,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,GAC1B,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA6C7E;AAGD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAkBrE;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlE"}
@@ -170,6 +170,9 @@ export { consultOverlay };
170
170
  // implements REQ-014
171
171
  export async function projectStagedEntities(prolog, results) {
172
172
  for (const { entity } of results) {
173
+ // Retract stale relationships before re-asserting entity
174
+ // (kb_assert_entity_no_audit preserves relationships on upsert)
175
+ await prolog.query(`kb_retract_entity_relationships('${entity.id}')`);
173
176
  const assertEntityResult = await prolog.query(buildEntityAssertionGoal(entity));
174
177
  if (!assertEntityResult.success) {
175
178
  throw new Error(`Failed to assert staged entity ${entity.id}: ${assertEntityResult.error || "unknown error"}`);
@@ -1,5 +1,5 @@
1
1
  export interface BaseRelationship {
2
- type: "depends_on" | "specified_by" | "verified_by" | "implements" | "covered_by" | "constrained_by" | "constrains" | "requires_property" | "requires_predicate" | "guards" | "publishes" | "consumes" | "supersedes" | "relates_to";
2
+ type: "depends_on" | "specified_by" | "verified_by" | "validates" | "implements" | "covered_by" | "executable_for" | "constrained_by" | "constrains" | "requires_property" | "requires_predicate" | "guards" | "publishes" | "consumes" | "supersedes" | "relates_to";
3
3
  from: string;
4
4
  to: string;
5
5
  created_at?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/types/relationships.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EACA,YAAY,GACZ,cAAc,GACd,aAAa,GACb,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,oBAAoB,GACpB,QAAQ,GACR,WAAW,GACX,UAAU,GACV,YAAY,GACZ,YAAY,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"relationships.d.ts","sourceRoot":"","sources":["../../src/types/relationships.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EACA,YAAY,GACZ,cAAc,GACd,aAAa,GACb,WAAW,GACX,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,YAAY,GACZ,mBAAmB,GACnB,oBAAoB,GACpB,QAAQ,GACR,WAAW,GACX,UAAU,GACV,YAAY,GACZ,YAAY,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAe,gBAAgB,CAAC"}
@@ -19,6 +19,7 @@ export interface KbConfigPaths {
19
19
  export interface KbConfig {
20
20
  paths: KbConfigPaths;
21
21
  schemaVersion?: number | string;
22
+ semanticAdvisorBackfill?: "pending" | "completed" | "not_applicable";
22
23
  /**
23
24
  * @deprecated defaultBranch is deprecated. Branch lifecycle now follows git naturally
24
25
  * without requiring a configured default. This field is ignored but kept for compatibility.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,YAAY,EAAE,YAAY,EAAE,yBAAyB,EAAE,CAAC;AAExD;;GAEG;AAEH,eAAO,MAAM,cAAc,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAgBxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAUhC,CAAC;AA4BF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAgChE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAgCpE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAoBA,OAAO,EACL,KAAK,YAAY,EAEjB,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AAG5B;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,aAAa,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,gBAAgB,CAAC;IACrE;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,YAAY,CAAC;CACvB;AAED,YAAY,EAAE,YAAY,EAAE,yBAAyB,EAAE,CAAC;AAExD;;GAEG;AAEH,eAAO,MAAM,cAAc,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAgBxD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,aAUhC,CAAC;AA4BF;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAmChE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAsB,GAAG,QAAQ,CAmCpE"}
@@ -98,6 +98,9 @@ export function loadConfig(cwd = process.cwd()) {
98
98
  ...(userConfig.defaultBranch !== undefined
99
99
  ? { defaultBranch: userConfig.defaultBranch }
100
100
  : {}),
101
+ ...(userConfig.semanticAdvisorBackfill !== undefined
102
+ ? { semanticAdvisorBackfill: userConfig.semanticAdvisorBackfill }
103
+ : {}),
101
104
  checks: userConfig.checks
102
105
  ? {
103
106
  rules: {
@@ -137,6 +140,9 @@ export function loadSyncConfig(cwd = process.cwd()) {
137
140
  ...(userConfig.defaultBranch !== undefined
138
141
  ? { defaultBranch: userConfig.defaultBranch }
139
142
  : {}),
143
+ ...(userConfig.semanticAdvisorBackfill !== undefined
144
+ ? { semanticAdvisorBackfill: userConfig.semanticAdvisorBackfill }
145
+ : {}),
140
146
  checks: userConfig.checks
141
147
  ? {
142
148
  rules: {
@@ -1,5 +1,5 @@
1
1
  import type { KbConfig } from "./config.js";
2
- export declare const LATEST_KB_SCHEMA_VERSION = 2;
2
+ export declare const LATEST_KB_SCHEMA_VERSION = 3;
3
3
  export interface SchemaVersionStatus {
4
4
  status: "missing" | "invalid" | "older" | "current" | "newer";
5
5
  currentVersion: number | null;
@@ -1,5 +1,5 @@
1
1
  // implements REQ-003
2
- export const LATEST_KB_SCHEMA_VERSION = 2;
2
+ export const LATEST_KB_SCHEMA_VERSION = 3;
3
3
  // implements REQ-003
4
4
  export function normalizeSchemaVersion(schemaVersion) {
5
5
  if (schemaVersion === undefined || schemaVersion === null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cli",
3
- "version": "0.12.5",
3
+ "version": "0.12.8",
4
4
  "type": "module",
5
5
  "description": "Kibi CLI for knowledge base management",
6
6
  "engines": {
@@ -99,7 +99,7 @@
99
99
  "fast-glob": "^3.2.12",
100
100
  "gray-matter": "^4.0.3",
101
101
  "js-yaml": "^4.1.0",
102
- "kibi-core": "^0.6.1",
102
+ "kibi-core": "^0.6.2",
103
103
  "ts-morph": "^23.0.0",
104
104
  "ignore": "^5.3.0"
105
105
  },
@@ -61,6 +61,15 @@
61
61
  "description": "[DEPRECATED] No longer used. Branch lifecycle now follows git naturally without requiring a configured default. This field is ignored but kept for backward compatibility.",
62
62
  "deprecated": true
63
63
  },
64
+ "schemaVersion": {
65
+ "type": ["integer", "string"],
66
+ "description": "Kibi KB schema version used by the migration command"
67
+ },
68
+ "semanticAdvisorBackfill": {
69
+ "type": "string",
70
+ "enum": ["pending", "completed", "not_applicable"],
71
+ "description": "Review state for retroactive semantic-advisor modeling of existing requirements"
72
+ },
64
73
  "checks": {
65
74
  "type": "object",
66
75
  "description": "Configuration for KB validation rules",
@@ -170,6 +170,12 @@ Anti-example: do not create a `flag` named `BUG-123` to track a defect. Create a
170
170
 
171
171
  Always confirm or create endpoint entities before linking them. Query target IDs with `kb_query` first. If an endpoint does not exist, create it with `kb_upsert` before creating the relationship. Creating relationships to non-existent entities produces dangling references that `kb_check` will flag.
172
172
 
173
+ For `kb_upsert`, keep relationship rows anchored to the entity you are upserting: each row's `from` must equal the upserted entity ID. If you need a `SYM -> REQ` link, upsert the symbol endpoint first, then link that symbol to the requirement.
174
+
175
+ Keep symbol payloads minimal: include only the fields needed to identify the symbol, its status, and its source traceability. Put extra prose, examples, or audit notes in docs or evidence files instead of custom `kb_upsert.properties`; strict `kb_upsert.properties` rejects unknown fields.
176
+
177
+ When a generic `Query failed` appears, do not keep retrying the same payload. First call `kb_validate_upsert`, query or create the missing endpoints, reduce the payload to the minimum required fields, and retry once. If it still fails, report the blocker instead of looping.
178
+
173
179
  ## Sequential Upserts
174
180
 
175
181
  Never fire `kb_upsert` calls in parallel. Execute them sequentially to avoid lock contention and ensure deterministic ordering. This is especially important when creating chains of related entities.
@@ -200,6 +206,7 @@ Call `kb_status` when you suspect the branch KB is stale or when switching conte
200
206
  | Missing `kb_check` | Undetected dangling refs and violations | Run targeted checks during work, full check at completion |
201
207
  | Tags as multi-ID lookup | Tags are metadata, not identifiers | Use `kb_query` with explicit `id` values |
202
208
  | `relates_to` for strict modeling | Loses contradiction safety | Use `constrains` and `requires_property` instead |
209
+ | `status: implemented` on requirements | Not a valid lifecycle status | Use a valid status such as `closed`, add an `implemented` tag, and link evidence instead |
203
210
 
204
211
  Before/after for reversed direction:
205
212