kibi-cli 2.17.0 → 2.19.0

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 (80) hide show
  1. package/dist/cli-operation-metadata.d.ts +1 -1
  2. package/dist/cli-operation-metadata.js +1 -1
  3. package/dist/cli-register-proof.js +9 -1
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +18 -6
  6. package/dist/commands/sync/manifest.d.ts +10 -0
  7. package/dist/commands/sync/manifest.d.ts.map +1 -1
  8. package/dist/commands/sync/manifest.js +27 -0
  9. package/dist/commands/sync/untracked-targets.d.ts +17 -0
  10. package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
  11. package/dist/commands/sync/untracked-targets.js +81 -9
  12. package/dist/commands/sync.d.ts.map +1 -1
  13. package/dist/commands/sync.js +76 -6
  14. package/dist/engine.d.ts +4 -0
  15. package/dist/engine.d.ts.map +1 -1
  16. package/dist/engine.js +56 -33
  17. package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
  18. package/dist/operations/modeling/predicate-applicability.js +5 -2
  19. package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
  20. package/dist/operations/modeling/predicate-catalog-4.js +4 -0
  21. package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
  22. package/dist/operations/modeling/predicate-loader.js +3 -0
  23. package/dist/operations/modeling/predicate-score-3.js +1 -1
  24. package/dist/operations/modeling/predicate-types.d.ts +5 -0
  25. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  26. package/dist/operations/mutation/source-authoring.d.ts +9 -0
  27. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  28. package/dist/operations/mutation/source-authoring.js +30 -1
  29. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  30. package/dist/operations/planning/apply-plan.js +10 -1
  31. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  32. package/dist/operations/semantic-advisor/clauses.js +7 -1
  33. package/dist/prolog/store-lock.d.ts +41 -0
  34. package/dist/prolog/store-lock.d.ts.map +1 -1
  35. package/dist/prolog/store-lock.js +91 -1
  36. package/dist/prolog.d.ts +7 -1
  37. package/dist/prolog.d.ts.map +1 -1
  38. package/dist/prolog.js +21 -3
  39. package/dist/proof/inspect.d.ts +8 -1
  40. package/dist/proof/inspect.d.ts.map +1 -1
  41. package/dist/proof/inspect.js +19 -2
  42. package/dist/public/operations/catalog.d.ts +50 -1
  43. package/dist/public/operations/catalog.d.ts.map +1 -1
  44. package/dist/public/operations/contracts.d.ts.map +1 -1
  45. package/dist/public/operations/contracts.js +4 -4
  46. package/dist/public/operations/discovery-executors.d.ts +50 -1
  47. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  48. package/dist/public/operations/discovery-executors.js +418 -8
  49. package/dist/public/operations/migration-plan.d.ts +2 -0
  50. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  51. package/dist/public/operations/migration-plan.js +31 -0
  52. package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
  53. package/dist/public/operations/source-relationship-parity.js +55 -1
  54. package/dist/public/operations/specs/discovery.d.ts +50 -1
  55. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  56. package/dist/public/operations/specs/discovery.js +56 -1
  57. package/dist/public/skills/kibi-usage/SKILL.md +20 -3
  58. package/dist/public/skills/kibi-usage/resources/proof.md +23 -0
  59. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  60. package/dist/runtime/cli-runtime.js +7 -2
  61. package/dist/search-answer.d.ts +12 -3
  62. package/dist/search-answer.d.ts.map +1 -1
  63. package/dist/search-answer.js +51 -4
  64. package/dist/utils/branch-store.d.ts +8 -0
  65. package/dist/utils/branch-store.d.ts.map +1 -1
  66. package/dist/utils/branch-store.js +27 -1
  67. package/dist/utils/content-freshness.d.ts +13 -0
  68. package/dist/utils/content-freshness.d.ts.map +1 -0
  69. package/dist/utils/content-freshness.js +171 -0
  70. package/dist/utils/symbol-manifest-layout.d.ts +9 -0
  71. package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
  72. package/dist/utils/symbol-manifest-layout.js +18 -0
  73. package/package.json +2 -2
  74. package/src/public/operations/contracts.ts +7 -4
  75. package/src/public/operations/discovery-executors.ts +556 -6
  76. package/src/public/operations/migration-plan.ts +44 -0
  77. package/src/public/operations/source-relationship-parity.ts +60 -3
  78. package/src/public/operations/specs/discovery.ts +67 -1
  79. package/src/public/skills/kibi-usage/SKILL.md +20 -3
  80. package/src/public/skills/kibi-usage/resources/proof.md +23 -0
package/dist/engine.js CHANGED
@@ -18,7 +18,7 @@ import { kibiPackageVersions } from "./package-versions.js";
18
18
  import { writePerformanceTraceEvent } from "./performance-trace.js";
19
19
  import { PrologProcess, resolveKbPlPath } from "./prolog.js";
20
20
  import { parseEntityFromList, parseListOfLists } from "./prolog/codec.js";
21
- import { retryAttachAfterBreakingStaleLock } from "./prolog/store-lock.js";
21
+ import { decideStoreLockTakeover, isProcessAlive, noteDeadHolderBeforeAttach, readRdfLockPid, recordStoreLockTakeover, retryAttachAfterBreakingStaleLock, } from "./prolog/store-lock.js";
22
22
  import { resolveSwipl, swiplIdentity } from "./prolog/swipl-resolver.js";
23
23
  import { queryEntityChunks } from "./public/operations/discovery-entities.js";
24
24
  import { isValidBranchName } from "./utils/branch-resolver.js";
@@ -34,6 +34,10 @@ const READ_LIMITED_METHODS = new Set([
34
34
  ]);
35
35
  export const ENGINE_PROTOCOL_VERSION = 1;
36
36
  export const ENGINE_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
37
+ /** Upper bound between two wall-clock idle checks of the engine daemon. */
38
+ export const ENGINE_IDLE_CHECK_INTERVAL_MS = 30 * 1000;
39
+ /** How long a stopping daemon waits for SWI to halt on stdin EOF. */
40
+ export const ENGINE_PROLOG_EXIT_GRACE_MS = 1000;
37
41
  /**
38
42
  * Package versions this engine build carries (KIBI_PACKAGE_VERSIONS when
39
43
  * set). The client sends them on every request and the daemon reports its
@@ -632,31 +636,19 @@ function recoverStaleJournalLock(branchPath) {
632
636
  const lockPath = path.join(branchPath, "rdf", "lock");
633
637
  if (!existsSync(lockPath))
634
638
  return;
635
- let contents = "";
636
- try {
637
- contents = readFileSync(lockPath, "utf8");
638
- }
639
- catch {
640
- return;
641
- }
642
- const match = /pid\((\d+)\)/.exec(contents);
643
- if (!match)
644
- return;
645
- const pid = Number.parseInt(match[1] ?? "", 10);
646
- if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid)
639
+ const pid = readRdfLockPid(lockPath);
640
+ if (pid === null || pid === process.pid || isProcessAlive(pid))
647
641
  return;
642
+ // implements REQ-core-journaled-engine-persistence
643
+ // Breaking a dead holder's lock is a takeover; record it for kb_status and
644
+ // kibi doctor instead of healing silently.
645
+ recordStoreLockTakeover(branchPath, pid, decideStoreLockTakeover({ pid }).reason);
648
646
  try {
649
- process.kill(pid, 0);
650
- return;
647
+ rmSync(lockPath, { force: true });
651
648
  }
652
649
  catch {
653
- try {
654
- rmSync(lockPath, { force: true });
655
- }
656
- catch {
657
- // Let rdf_attach_db report the lock error if another process wins the
658
- // race between the liveness probe and removal.
659
- }
650
+ // Let rdf_attach_db report the lock error if another process wins the
651
+ // race between the liveness probe and removal.
660
652
  }
661
653
  }
662
654
  function daemonEntry() {
@@ -1561,8 +1553,13 @@ export class EngineSession {
1561
1553
  }
1562
1554
  async boot() {
1563
1555
  await this.prolog.start();
1556
+ // A killed holder never makes kb_attach fail (its OS lock died with it),
1557
+ // so a dead-holder takeover must be noted before the attach succeeds.
1558
+ const recordTakeover = noteDeadHolderBeforeAttach(this.branchPath);
1564
1559
  const attached = await this.prolog.query(`kb_attach('${quoteProlog(this.branchPath)}')`);
1565
- if (!attached.success) {
1560
+ if (attached.success)
1561
+ recordTakeover();
1562
+ else {
1566
1563
  // The daemon hosts the branch store for every later request, so a
1567
1564
  // store-locked startup with a provably dead holder is auto-healed here
1568
1565
  // exactly like the CLI runtime path; live holders abort the startup
@@ -1695,7 +1692,12 @@ export async function runEngineDaemon(requestedOptions) {
1695
1692
  writeFileSync(enginePidPath(options.workspaceRoot, options.branch), `${process.pid}\n`, { mode: 0o600 });
1696
1693
  const server = net.createServer();
1697
1694
  let activeClients = 0;
1698
- let idleTimer = null;
1695
+ // implements REQ-core-journaled-engine-persistence
1696
+ // Idle exit compares wall-clock time: a one-shot timer does not advance
1697
+ // while the host (a VM, a laptop) is suspended, so a daemon would outlive
1698
+ // its idle budget by the whole suspension.
1699
+ let lastActivityAt = Date.now();
1700
+ let idleWatch = null;
1699
1701
  let queue = Promise.resolve();
1700
1702
  let idleCompactionQueued = false;
1701
1703
  const queryCache = new Map();
@@ -1783,12 +1785,17 @@ export async function runEngineDaemon(requestedOptions) {
1783
1785
  idleCompactionQueued = false;
1784
1786
  });
1785
1787
  }
1786
- if (idleTimer)
1787
- clearTimeout(idleTimer);
1788
- idleTimer = setTimeout(() => {
1789
- if (activeClients === 0)
1788
+ lastActivityAt = Date.now();
1789
+ if (idleWatch)
1790
+ return;
1791
+ const idleMs = engineIdleTimeoutMs();
1792
+ idleWatch = setInterval(() => {
1793
+ if (!shuttingDown &&
1794
+ activeClients === 0 &&
1795
+ Date.now() - lastActivityAt >= idleMs)
1790
1796
  void shutdown();
1791
- }, engineIdleTimeoutMs());
1797
+ }, Math.min(idleMs, ENGINE_IDLE_CHECK_INTERVAL_MS));
1798
+ idleWatch.unref();
1792
1799
  };
1793
1800
  const handle = async (request, cancelledRequests) => {
1794
1801
  if (request.protocolVersion !== ENGINE_PROTOCOL_VERSION) {
@@ -2121,8 +2128,8 @@ export async function runEngineDaemon(requestedOptions) {
2121
2128
  shuttingDown = true;
2122
2129
  process.off("SIGTERM", requestSignalShutdown);
2123
2130
  process.off("SIGINT", requestSignalShutdown);
2124
- if (idleTimer)
2125
- clearTimeout(idleTimer);
2131
+ if (idleWatch)
2132
+ clearInterval(idleWatch);
2126
2133
  clearInterval(workspaceWatchdog);
2127
2134
  // Stop accepting clients before the first asynchronous durability step.
2128
2135
  // Keep the PID published until Prolog releases the branch-store lock.
@@ -2131,6 +2138,7 @@ export async function runEngineDaemon(requestedOptions) {
2131
2138
  const saved = session.needsRecycle()
2132
2139
  ? null
2133
2140
  : await prolog.query("kb_save").catch(() => null);
2141
+ let detached = false;
2134
2142
  if (saved?.success) {
2135
2143
  try {
2136
2144
  fsyncJournaledBranchStore(branchPath);
@@ -2139,8 +2147,22 @@ export async function runEngineDaemon(requestedOptions) {
2139
2147
  // Shutdown remains best effort. Request-time writes fail closed if
2140
2148
  // their durability barrier cannot complete.
2141
2149
  }
2142
- }
2143
- await prolog.terminate().catch(() => undefined);
2150
+ // implements REQ-core-journaled-engine-persistence
2151
+ // Release the store the way the one-shot path does: kb_detach removes
2152
+ // rdf/lock and the ownership journal. A SIGTERM'd SWI process never
2153
+ // runs its at_halt cleanup, so skipping this left a dead lock behind
2154
+ // on every stop.
2155
+ detached = await prolog
2156
+ .query("kb_detach")
2157
+ .then((result) => result.success)
2158
+ .catch(() => false);
2159
+ }
2160
+ // Once kb_detach released the store, nothing in at_halt is needed and the
2161
+ // child is signalled at once. Otherwise let SWI halt on stdin EOF (running
2162
+ // its at_halt cleanup) for a bounded moment; the wait ends on exit.
2163
+ await prolog
2164
+ .terminate({ graceMs: detached ? 0 : ENGINE_PROLOG_EXIT_GRACE_MS })
2165
+ .catch(() => undefined);
2144
2166
  for (const client of clients)
2145
2167
  client.destroy();
2146
2168
  // A close callback can remain pending on older Node releases when a
@@ -2189,6 +2211,7 @@ export async function runEngineDaemon(requestedOptions) {
2189
2211
  const bytes = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
2190
2212
  buffer = parseFrames(Buffer.concat([buffer, bytes]), (value) => {
2191
2213
  const request = value;
2214
+ lastActivityAt = Date.now();
2192
2215
  if (shuttingDown) {
2193
2216
  writeSocketFrame(socket, {
2194
2217
  id: request.id,
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-applicability.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-applicability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AA2W1C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,MAAM,GACX,qBAAqB,CA2EvB"}
1
+ {"version":3,"file":"predicate-applicability.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-applicability.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AA8W1C,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,MAAM,GACX,qBAAqB,CA2EvB"}
@@ -2,7 +2,10 @@ import { usageHintsForSchema, } from "./predicate-ranker.js";
2
2
  export const MIN_APPLICABILITY_SCORE = 0.62;
3
3
  export const STRONG_APPLICABILITY_SCORE = 0.8;
4
4
  export const WEAK_CANDIDATE_MARGIN = 0.06;
5
- const NORMATIVE_OR_VALIDITY_PATTERN = /\b(?:must|shall|should|required|requires?|may\s+only|only\s+.+?\s+(?:may|can)|must\s+not|shall\s+not|cannot|can't|allowed|denied|forbidden|prohibited|invalid|unresolved|reject(?:s|ed|ion)?|fail(?:s|ed|ure)?\s+(?:clearly|explicitly|with)|defaults?\s+to|states?\s+are|owned\s+by|initializes?\s+after|before|unless|when|if|(?:exception|exempt)\s+(?:to|from|when|if|unless))\b/i;
5
+ // A bare permission modal ("coaches can read", "users may export", "are
6
+ // permitted to") states a permission as much as "allowed" does, so it must
7
+ // reach the permission_rule intent rule instead of being rejected here.
8
+ const NORMATIVE_OR_VALIDITY_PATTERN = /\b(?:must|shall|should|required|requires?|may\s+only|only\s+.+?\s+(?:may|can)|(?:can|may)\s+(?:not\s+)?(?:only\s+)?\w+|(?:is|are)\s+(?:not\s+)?(?:allowed|permitted)\s+to|permitted|must\s+not|shall\s+not|cannot|can't|allowed|denied|forbidden|prohibited|invalid|unresolved|reject(?:s|ed|ion)?|fail(?:s|ed|ure)?\s+(?:clearly|explicitly|with)|defaults?\s+to|states?\s+are|owned\s+by|initializes?\s+after|before|unless|when|if|(?:exception|exempt)\s+(?:to|from|when|if|unless))\b/i;
6
9
  function expressesNormativeOrValidityIntent(text) {
7
10
  return /^\s*no\b/i.test(text) || NORMATIVE_OR_VALIDITY_PATTERN.test(text);
8
11
  }
@@ -95,7 +98,7 @@ const INTENT_RULES = {
95
98
  },
96
99
  permission_rule: {
97
100
  allOf: [
98
- [/only|may|can|allowed|denied|forbidden|must\s+not|cannot/i],
101
+ [/only|may|can|allowed|permitted|denied|forbidden|must\s+not|cannot/i],
99
102
  [
100
103
  /delete|export|access|read|write|update|remove|manage|permission/i,
101
104
  // A common action verb counts only right after the permission modal
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-catalog-4.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-4.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAqTzD,CAAC"}
1
+ {"version":3,"file":"predicate-catalog-4.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-4.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAyTzD,CAAC"}
@@ -203,10 +203,14 @@ export const PREDICATE_CATALOG_4 = [
203
203
  argument_names: ["actor", "action", "resource", "decision"],
204
204
  argument_types: ["actor", "action", "resource", "decision"],
205
205
  ...closedArguments({ decision: DECISION_VOCABULARY }),
206
+ // One decision per actor, action and resource: an allow and a deny for
207
+ // the same three contradict (kb.pl builtin_decision_predicate/5).
208
+ key_arguments: ["actor", "action", "resource"],
206
209
  keywords: [
207
210
  "may",
208
211
  "can",
209
212
  "allowed",
213
+ "permitted",
210
214
  "denied",
211
215
  "forbidden",
212
216
  "must not",
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-loader.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAK3E,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAI9B,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAExE;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CACxE,wBAAwB,EACxB,UAAU,CACX,GAAG;IACF,WAAW,EAAE,mBAAmB,CAAC;CAClC,CA4BA;AAGD,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,sBAAsB,EAAE,OAAO,EAC/B,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAwBrC;AAGD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,wBAAwB,EAAE,CAwD5B;AAGD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,mBAAmB,GAAG,SAAS,CAOjC;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQpD"}
1
+ {"version":3,"file":"predicate-loader.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-loader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAK3E,OAAO,KAAK,EACV,wBAAwB,EACxB,mBAAmB,EACpB,MAAM,sBAAsB,CAAC;AAI9B,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAExE;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CACxE,wBAAwB,EACxB,UAAU,CACX,GAAG;IACF,WAAW,EAAE,mBAAmB,CAAC;CAClC,CA6BA;AAGD,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,sBAAsB,EAAE,OAAO,EAC/B,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAwBrC;AAGD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,wBAAwB,EAAE,CA0D5B;AAGD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,mBAAmB,GAAG,SAAS,CAOjC;AAGD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAQpD"}
@@ -23,6 +23,7 @@ export function schemaForCandidate(schema) {
23
23
  ...(schema.argument_aliases
24
24
  ? { argument_aliases: schema.argument_aliases }
25
25
  : {}),
26
+ ...(schema.key_arguments ? { key_arguments: schema.key_arguments } : {}),
26
27
  ...(schema.aliases ? { aliases: schema.aliases } : {}),
27
28
  ...(schema.paraphrase_templates
28
29
  ? { paraphrase_templates: schema.paraphrase_templates }
@@ -64,6 +65,7 @@ export function predicateSchemaFromEntity(entity) {
64
65
  return [];
65
66
  const usageHints = usageHintsFromEntity(entity);
66
67
  const argumentDescriptions = stringArray(entity.argument_descriptions);
68
+ const keyArguments = stringArray(entity.key_arguments);
67
69
  const vocabulary = predicateVocabularyFromEntity(entity);
68
70
  return [
69
71
  {
@@ -93,6 +95,7 @@ export function predicateSchemaFromEntity(entity) {
93
95
  ])),
94
96
  }
95
97
  : {}),
98
+ ...(keyArguments.length > 0 ? { key_arguments: keyArguments } : {}),
96
99
  keywords: [
97
100
  predicateName,
98
101
  ...stringArray(entity.aliases),
@@ -51,7 +51,7 @@ export function scoreExactPredicates3(schema, text) {
51
51
  return 0;
52
52
  }
53
53
  if (schema.predicate_name === "permission_rule") {
54
- if (/^(?:only\s+)?[a-z][a-z\s_-]*\s+(?:may|can|is\s+allowed\s+to|must\s+not|cannot|can't|is\s+forbidden\s+to)\s+.+?\.?$/i.test(text)) {
54
+ if (/^(?:only\s+)?[a-z][a-z\s_-]*\s+(?:may|can|(?:is|are)\s+(?:not\s+)?(?:allowed|permitted|forbidden)\s+to|must\s+not|cannot|can't)\s+.+?\.?$/i.test(text)) {
55
55
  return 0.98;
56
56
  }
57
57
  return 0;
@@ -16,6 +16,11 @@ export interface PredicateSchemaCandidate {
16
16
  argument_constants?: Record<string, string[]>;
17
17
  /** Legacy spellings keyed by argument name, each mapped to a constant. */
18
18
  argument_aliases?: Record<string, Record<string, string>>;
19
+ /**
20
+ * Argument names that determine the remaining arguments (a functional
21
+ * dependency). Two facts that agree on them describe the same case.
22
+ */
23
+ key_arguments?: string[];
19
24
  keywords: string[];
20
25
  /** Human-language aliases and controlled paraphrase templates used for deterministic retrieval. */
21
26
  aliases?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-types.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"predicate-types.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-types.ts"],"names":[],"mappings":"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"}
@@ -80,6 +80,15 @@ export declare function renderYamlRelationshipDeletion(sourcePathValue: string,
80
80
  * so a plan writes exactly what `kb_upsert` would.
81
81
  */
82
82
  export declare function renderSourceDocument(input: UpsertInput, entity: Readonly<Record<string, unknown>>, existingContent: string | undefined, relativePath: string): string;
83
+ /**
84
+ * Whether Git definitely does not track `relative`. Sync reads only tracked
85
+ * sources plus pending-source receipts, so an untracked file (for example the
86
+ * `.kb/symbols.yaml` that `kibi init` creates) needs a receipt on every
87
+ * write, or a new branch store compiles relationships without the entities
88
+ * they connect. Any other Git outcome (not a repository, git missing) is
89
+ * "unknown" and keeps the previous behaviour.
90
+ */
91
+ export declare function isUntrackedByGit(workspaceRoot: string, relative: string): boolean;
83
92
  export declare function writeSourceForUpsert(input: UpsertInput, entity: Readonly<Record<string, unknown>>, existing?: Readonly<Record<string, unknown>>, context?: OperationContext, sourceDocumentOverride?: string): Promise<{
84
93
  receipt: SourceWriteReceipt;
85
94
  rollback: () => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAMjF,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAmBH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,IAAI,CAiBN;AAED,KAAK,SAAS,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAExD,8EAA8E;AAE9E,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAOpE;AAED;;;;GAIG;AAEH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,SAAS,GAAG,IAAI,GACtB,IAAI,CAON;AAeD;;;;GAIG;AAEH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,IAAI,CAMN;AAgBD,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CAqCR;AAED,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,CAoBR;AAsBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAkGD,uFAAuF;AAEvF,eAAO,MAAM,uBAAuB,uGAM1B,CAAC;AA8BX,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiCzC;AAoRD,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB;IAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA6C7C;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4DpC;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiEpC;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GACnB,MAAM,CAsBR;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC5C,OAAO,CAAC,EAAE,gBAAgB,EAC1B,sBAAsB,CAAC,EAAE,MAAM,GAC9B,OAAO,CAAC;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,GAAG,IAAI,CAAC,CA6IR;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC3C,MAAM,CAOR"}
1
+ {"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAOjF,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAmBH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,IAAI,CAiBN;AAED,KAAK,SAAS,GAAG,QAAQ,CAAC;IAAE,KAAK,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAExD,8EAA8E;AAE9E,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAOpE;AAED;;;;GAIG;AAEH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,SAAS,GAAG,IAAI,GACtB,IAAI,CAON;AAeD;;;;GAIG;AAEH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,IAAI,CAMN;AAgBD,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CAqCR;AAED,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,CAoBR;AAsBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAkGD,uFAAuF;AAEvF,eAAO,MAAM,uBAAuB,uGAM1B,CAAC;AA8BX,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiCzC;AAwRD,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB;IAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA6C7C;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4DpC;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiEpC;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GACnB,MAAM,CAsBR;AAED;;;;;;;GAOG;AAEH,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAaT;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC5C,OAAO,CAAC,EAAE,gBAAgB,EAC1B,sBAAsB,CAAC,EAAE,MAAM,GAC9B,OAAO,CAAC;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,GAAG,IAAI,CAAC,CA+IR;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC3C,MAAM,CAOR"}
@@ -1,3 +1,4 @@
1
+ import { execFileSync } from "node:child_process";
1
2
  import { createHash } from "node:crypto";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import fs from "node:fs";
@@ -9,6 +10,7 @@ import { OperationError } from "../../cli-errors.js";
9
10
  import { requirementSemanticText } from "../../extractors/markdown.js";
10
11
  import { canonicalFilesystemPath } from "../../utils/canonical-path.js";
11
12
  import { CANONICAL_ENTITY_PATHS, isDerivedKbPath, } from "../../utils/kb-paths.js";
13
+ import { blockifySymbolManifest } from "../../utils/symbol-manifest-layout.js";
12
14
  function digest(value) {
13
15
  return createHash("sha256").update(value).digest("hex");
14
16
  }
@@ -462,6 +464,10 @@ function renderSymbolManifest(entity, relationships, existingContent) {
462
464
  if (!symbols || typeof symbols !== "object" || !("items" in symbols)) {
463
465
  throw new OperationError("SOURCE_FORMAT_INVALID", "Symbol manifest must contain a symbols array");
464
466
  }
467
+ // implements REQ-core-persistence
468
+ // Render the list (and the entries that were written inside it) as block
469
+ // YAML so diffs stay reviewable.
470
+ blockifySymbolManifest(doc);
465
471
  const items = symbols.items;
466
472
  const id = String(entity.id);
467
473
  const indices = items.flatMap((item, index) => {
@@ -670,6 +676,27 @@ export function renderSourceDocument(input, entity, existingContent, relativePat
670
676
  }
671
677
  return renderEntityDocument(input, entity, existingContent);
672
678
  }
679
+ /**
680
+ * Whether Git definitely does not track `relative`. Sync reads only tracked
681
+ * sources plus pending-source receipts, so an untracked file (for example the
682
+ * `.kb/symbols.yaml` that `kibi init` creates) needs a receipt on every
683
+ * write, or a new branch store compiles relationships without the entities
684
+ * they connect. Any other Git outcome (not a repository, git missing) is
685
+ * "unknown" and keeps the previous behaviour.
686
+ */
687
+ // implements REQ-core-persistence
688
+ export function isUntrackedByGit(workspaceRoot, relative) {
689
+ try {
690
+ execFileSync("git", ["-C", workspaceRoot, "ls-files", "--error-unmatch", "--", relative],
691
+ // Bounded: a write must never wait on Git. A timeout reads as
692
+ // "tracked", the pre-receipt behaviour.
693
+ { stdio: "ignore", timeout: 10_000 });
694
+ return false;
695
+ }
696
+ catch (error) {
697
+ return error.status === 1;
698
+ }
699
+ }
673
700
  export async function writeSourceForUpsert(input, entity, existing, context, sourceDocumentOverride) {
674
701
  if (!context?.fs)
675
702
  return null;
@@ -795,7 +822,9 @@ export async function writeSourceForUpsert(input, entity, existing, context, sou
795
822
  };
796
823
  try {
797
824
  if (receipt.afterHash !== null &&
798
- (before === undefined || fs.existsSync(pendingReceipt))) {
825
+ (before === undefined ||
826
+ fs.existsSync(pendingReceipt) ||
827
+ isUntrackedByGit(context.workspaceRoot, relative))) {
799
828
  writePendingSourceReceipt(context.workspaceRoot, relative, receipt.afterHash);
800
829
  }
801
830
  }
@@ -1 +1 @@
1
- {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AASlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AAgE/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AA8zBD;;;;;;GAMG;AAEH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAyqDD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,eAAe,CAAC;CACpC,CAAC,CAkDD"}
1
+ {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AASlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AAgE/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AA8zBD;;;;;;GAMG;AAEH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAyqDD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,eAAe,CAAC;CACpC,CAAC,CAkDD"}
@@ -7,7 +7,7 @@ import { syncCommand } from "../../commands/sync.js";
7
7
  import { PROOF_INTEGRATION_CONFIGURE, applyProofIntegrationAction, } from "../../proof/integration-plan.js";
8
8
  import { loadEntities } from "../../public/operations/discovery-entities.js";
9
9
  import { executeStatus } from "../../public/operations/discovery-executors.js";
10
- import { PREDICATE_SCHEMA_ALIGNMENT_CODE, migrationPlanHash, } from "../../public/operations/migration-plan.js";
10
+ import { FRESHNESS_RESYNC, PREDICATE_SCHEMA_ALIGNMENT_CODE, migrationPlanHash, } from "../../public/operations/migration-plan.js";
11
11
  import { readMigrationConfigStatus } from "../../public/operations/migration-plan.js";
12
12
  import { readWorkspaceSnapshot } from "../../public/operations/workspace-snapshot.js";
13
13
  import { computeShardPath, renderShardWithRelationship, } from "../../relationships/shards.js";
@@ -2305,6 +2305,15 @@ async function applyMigrationAction(action, context) {
2305
2305
  throw new Error("Compiling the branch store (kibi sync) did not complete successfully.");
2306
2306
  return;
2307
2307
  }
2308
+ // implements REQ-cli-status-pre-first-sync
2309
+ case FRESHNESS_RESYNC: {
2310
+ const result = await syncCommand({
2311
+ workspaceRoot: context.workspaceRoot,
2312
+ });
2313
+ if (!result.success)
2314
+ throw new Error("Recompiling changed sources (kibi sync) did not complete successfully.");
2315
+ return;
2316
+ }
2308
2317
  case "damaged_exact_branch_store":
2309
2318
  await branchRecoverCommand({
2310
2319
  apply: true,
@@ -1 +1 @@
1
- {"version":3,"file":"clauses.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/clauses.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;CACjC,CAAC,CAAC;AAYH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAQD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB,WAAW,GAAG,aAAa,CAG7B;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AA4BD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,GAClC,SAAS,cAAc,EAAE,CA4B3B"}
1
+ {"version":3,"file":"clauses.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/clauses.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,MAAM,EAAE,UAAU,GAAG,UAAU,CAAC;CACjC,CAAC,CAAC;AAkBH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAQD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB,WAAW,GAAG,aAAa,CAG7B;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW7D;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAIrD;AA4BD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,GAClC,SAAS,cAAc,EAAE,CA4B3B"}
@@ -1,5 +1,11 @@
1
1
  import { createHash } from "node:crypto";
2
- const NORMATIVE_ASSERTION_PATTERN = /\b(?:must|shall|should|required|requires?|may\s+only|may\s+(?:not\s+)?(?:be\s+)?\w+\s+only\b|may\s+(?:have\s+)?(?:at\s+most|at\s+least|exactly|no\s+more\s+than|up\s+to)|only\s+.+?\s+(?:may|can)|must\s+not|shall\s+not|cannot|can't|denied|forbidden|prohibited|(?:is|are|be|become|becomes|remain|remains)\s+(?:invalid|rejected|prohibited|forbidden)|(?:reject|rejects|rejected|rejection)\s+(?:invalid|unresolved|ambiguous)|fail(?:s|ed)?\s+(?:clearly|explicitly|with)|expires?\s+(?:after|within|in)|failure\s+(?:behavior|policy|outcome)|error\s+(?:handling|behavior|policy|outcome)|required\s+outcome|defaults?\s+to)\b/i;
2
+ // An explicit grant ("are allowed to", "is permitted to") asserts a
3
+ // permission just as "denied"/"forbidden" assert a prohibition. Bare "can" and
4
+ // "may" are accepted by the predicate applicability gate but not here: stored
5
+ // clause roles are derived from this pattern, and "it can be restricted to …"
6
+ // style capability prose would turn existing descriptive inventories into
7
+ // unmodeled normative clauses.
8
+ const NORMATIVE_ASSERTION_PATTERN = /\b(?:must|shall|should|required|requires?|may\s+only|(?:is|are)\s+(?:not\s+)?(?:allowed|permitted)\s+to|permitted|may\s+(?:not\s+)?(?:be\s+)?\w+\s+only\b|may\s+(?:have\s+)?(?:at\s+most|at\s+least|exactly|no\s+more\s+than|up\s+to)|only\s+.+?\s+(?:may|can)|must\s+not|shall\s+not|cannot|can't|denied|forbidden|prohibited|(?:is|are|be|become|becomes|remain|remains)\s+(?:invalid|rejected|prohibited|forbidden)|(?:reject|rejects|rejected|rejection)\s+(?:invalid|unresolved|ambiguous)|fail(?:s|ed)?\s+(?:clearly|explicitly|with)|expires?\s+(?:after|within|in)|failure\s+(?:behavior|policy|outcome)|error\s+(?:handling|behavior|policy|outcome)|required\s+outcome|defaults?\s+to)\b/i;
3
9
  // Conditional/temporal cues help discover clauses but do not by themselves
4
10
  // turn explanatory or subjective context into an asserted obligation.
5
11
  const NORMATIVE_PATTERN = new RegExp(`${NORMATIVE_ASSERTION_PATTERN.source}|\\b(?:before|unless|when|if)\\b`, "i");
@@ -21,6 +21,47 @@ export declare function readStoreLockOwner(storePath: string): {
21
21
  * lives at the branch-store root (older builds kept it beside the lock).
22
22
  */
23
23
  export declare function breakStoreLock(rdfDirectory: string): void;
24
+ /** Store-root record of the last stale-lock takeover (see below). */
25
+ export declare const STORE_LOCK_TAKEOVER_FILE = ".kibi-lock-takeover.json";
26
+ /**
27
+ * How long a takeover stays visible in kb_status and kibi doctor. A takeover
28
+ * happens inside a detached engine whose output nobody reads, so the record
29
+ * outlives that engine (which may idle out within minutes) long enough to be
30
+ * seen on the next working session; the next takeover replaces it.
31
+ */
32
+ export declare const STORE_LOCK_TAKEOVER_VISIBLE_MS: number;
33
+ export type StoreLockTakeoverRecord = Readonly<{
34
+ brokenPid: number | null;
35
+ reason: string;
36
+ at: string;
37
+ path: string;
38
+ }>;
39
+ /**
40
+ * Record that a stale branch-store lock is about to be broken. Best effort:
41
+ * the record is diagnostic and must never block the takeover itself.
42
+ */
43
+ export declare function recordStoreLockTakeover(storePath: string, brokenPid: number | null, reason: string, at?: Date): void;
44
+ /** The last takeover record of a branch store, or null when none is readable. */
45
+ export declare function readStoreLockTakeover(storePath: string): StoreLockTakeoverRecord | null;
46
+ /** The holder pid an rdf_persistency `lock` file records, or null. */
47
+ export declare function readRdfLockPid(lockPath: string): number | null;
48
+ /**
49
+ * Note, before `kb_attach`, whether a dead holder left this branch store
50
+ * locked, and return the step that records the takeover once the attach has
51
+ * succeeded.
52
+ *
53
+ * rdf_persistency locks the store with an OS advisory lock that the kernel
54
+ * releases when its holder dies, so a killed engine never makes the next
55
+ * attach fail with `store_locked`: the attach simply takes the store over.
56
+ * The only trace is the `rdf/lock` file, which SWI deletes on every detach
57
+ * and halt; one that is still present was left by a holder that crashed or
58
+ * was killed. Its holder (the ownership journal when it names the same
59
+ * process, which adds the boot id, else the lock file's pid) is classified
60
+ * the same way a `store_locked` failure is. The returned step is a no-op
61
+ * unless that holder is provably dead; callers run it only after a
62
+ * successful attach, which proves the holder no longer held the lock.
63
+ */
64
+ export declare function noteDeadHolderBeforeAttach(kbPath: string): () => void;
24
65
  export interface StoreLockTakeoverDecision {
25
66
  readonly action: "break" | "surface";
26
67
  readonly reason: string;
@@ -1 +1 @@
1
- {"version":3,"file":"store-lock.d.ts","sourceRoot":"","sources":["../../src/prolog/store-lock.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAqB,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAgBhF,8EAA8E;AAC9E,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAa7C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEzE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,oBAAoB,GAAG,IAAI,GACjC,oBAAoB,CAYtB;AAED;;;;;GAKG;AAEH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,oBAAoB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4B7D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAWzD;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,oBAAoB,GAAG,IAAI,GACjC,yBAAyB,CAoB3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACxD;AAED;;;;;;GAMG;AAEH,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC,CA4BtB"}
1
+ {"version":3,"file":"store-lock.d.ts","sourceRoot":"","sources":["../../src/prolog/store-lock.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,KAAK,EAAqB,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAgBhF,8EAA8E;AAC9E,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAa7C;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAUnD;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEzE,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,oBAAoB,GAAG,IAAI,GACjC,oBAAoB,CAYtB;AAED;;;;;GAKG;AAEH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,oBAAoB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA4B7D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAWzD;AAED,qEAAqE;AACrE,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,QAAsB,CAAC;AAElE,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC,CAAC;AAEH;;;GAGG;AAEH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,MAAM,EAAE,MAAM,EACd,EAAE,GAAE,IAAiB,GACpB,IAAI,CAUN;AAED,iFAAiF;AAEjF,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAChB,uBAAuB,GAAG,IAAI,CAmBhC;AAED,sEAAsE;AAEtE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQ9D;AAED;;;;;;;;;;;;;;;GAeG;AAEH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,CAerE;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,oBAAoB,GAAG,IAAI,GACjC,yBAAyB,CAoB3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACxD;AAED;;;;;;GAMG;AAEH,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,qBAAqB,EAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC,CAiCtB"}
@@ -15,7 +15,7 @@
15
15
  You should have received a copy of the GNU Affero General Public License
16
16
  along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
- import { existsSync, rmSync } from "node:fs";
18
+ import { existsSync, rmSync, writeFileSync } from "node:fs";
19
19
  import { readFileSync } from "node:fs";
20
20
  import { dirname, join } from "node:path";
21
21
  import { escapeAtom } from "./codec.js";
@@ -133,6 +133,95 @@ export function breakStoreLock(rdfDirectory) {
133
133
  }
134
134
  }
135
135
  }
136
+ /** Store-root record of the last stale-lock takeover (see below). */
137
+ export const STORE_LOCK_TAKEOVER_FILE = ".kibi-lock-takeover.json";
138
+ /**
139
+ * How long a takeover stays visible in kb_status and kibi doctor. A takeover
140
+ * happens inside a detached engine whose output nobody reads, so the record
141
+ * outlives that engine (which may idle out within minutes) long enough to be
142
+ * seen on the next working session; the next takeover replaces it.
143
+ */
144
+ export const STORE_LOCK_TAKEOVER_VISIBLE_MS = 24 * 60 * 60 * 1000;
145
+ /**
146
+ * Record that a stale branch-store lock is about to be broken. Best effort:
147
+ * the record is diagnostic and must never block the takeover itself.
148
+ */
149
+ // implements REQ-core-journaled-engine-persistence
150
+ export function recordStoreLockTakeover(storePath, brokenPid, reason, at = new Date()) {
151
+ try {
152
+ writeFileSync(join(storePath, STORE_LOCK_TAKEOVER_FILE), `${JSON.stringify({ brokenPid, reason, at: at.toISOString() })}\n`, { mode: 0o600 });
153
+ }
154
+ catch {
155
+ // A read-only or vanished store still gets its lock broken.
156
+ }
157
+ }
158
+ /** The last takeover record of a branch store, or null when none is readable. */
159
+ // implements REQ-core-journaled-engine-persistence
160
+ export function readStoreLockTakeover(storePath) {
161
+ const recordPath = join(storePath, STORE_LOCK_TAKEOVER_FILE);
162
+ if (!existsSync(recordPath))
163
+ return null;
164
+ try {
165
+ const parsed = JSON.parse(readFileSync(recordPath, "utf8"));
166
+ if (typeof parsed.at !== "string" || Number.isNaN(Date.parse(parsed.at)))
167
+ return null;
168
+ return {
169
+ brokenPid: typeof parsed.brokenPid === "number" ? parsed.brokenPid : null,
170
+ reason: typeof parsed.reason === "string" ? parsed.reason : "",
171
+ at: parsed.at,
172
+ path: recordPath,
173
+ };
174
+ }
175
+ catch {
176
+ return null;
177
+ }
178
+ }
179
+ /** The holder pid an rdf_persistency `lock` file records, or null. */
180
+ // implements REQ-core-journaled-engine-persistence
181
+ export function readRdfLockPid(lockPath) {
182
+ try {
183
+ const match = /pid\((\d+)\)/.exec(readFileSync(lockPath, "utf8"));
184
+ const pid = Number.parseInt(match?.[1] ?? "", 10);
185
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ }
191
+ /**
192
+ * Note, before `kb_attach`, whether a dead holder left this branch store
193
+ * locked, and return the step that records the takeover once the attach has
194
+ * succeeded.
195
+ *
196
+ * rdf_persistency locks the store with an OS advisory lock that the kernel
197
+ * releases when its holder dies, so a killed engine never makes the next
198
+ * attach fail with `store_locked`: the attach simply takes the store over.
199
+ * The only trace is the `rdf/lock` file, which SWI deletes on every detach
200
+ * and halt; one that is still present was left by a holder that crashed or
201
+ * was killed. Its holder (the ownership journal when it names the same
202
+ * process, which adds the boot id, else the lock file's pid) is classified
203
+ * the same way a `store_locked` failure is. The returned step is a no-op
204
+ * unless that holder is provably dead; callers run it only after a
205
+ * successful attach, which proves the holder no longer held the lock.
206
+ */
207
+ // implements REQ-core-journaled-engine-persistence
208
+ export function noteDeadHolderBeforeAttach(kbPath) {
209
+ const lockPath = join(kbPath, "rdf", "lock");
210
+ if (!existsSync(lockPath))
211
+ return () => undefined;
212
+ const lockPid = readRdfLockPid(lockPath);
213
+ const journal = readStoreLockOwner(kbPath)?.owner ?? null;
214
+ const owner = journal !== null && (lockPid === null || journal.pid === lockPid)
215
+ ? journal
216
+ : lockPid !== null
217
+ ? { pid: lockPid }
218
+ : null;
219
+ const decision = decideStoreLockTakeover(owner);
220
+ if (decision.action !== "break")
221
+ return () => undefined;
222
+ const brokenPid = owner?.pid ?? null;
223
+ return () => recordStoreLockTakeover(kbPath, brokenPid, decision.reason);
224
+ }
136
225
  /**
137
226
  * Decide whether a store-locked failure may be auto-healed. Only a provably
138
227
  * dead holder (or a holder from a previous boot) is broken; live or unknown
@@ -186,6 +275,7 @@ Store lock holder: ${holderSummary}. Close that session, or run 'kibi engine sto
186
275
  const lockDirectory = storeLocked.lockDirectory !== ""
187
276
  ? storeLocked.lockDirectory
188
277
  : join(kbPath, "rdf");
278
+ recordStoreLockTakeover(kbPath, storeLocked.owner?.pid ?? null, decision.reason);
189
279
  breakStoreLock(lockDirectory);
190
280
  const retried = await prolog.query(`kb_attach('${escapeAtom(kbPath)}')`);
191
281
  if (retried.success) {
package/dist/prolog.d.ts CHANGED
@@ -122,6 +122,12 @@ export declare class PrologProcess {
122
122
  isRunning(): boolean;
123
123
  getPid(): number;
124
124
  private lostProcessError;
125
- terminate(): Promise<void>;
125
+ /**
126
+ * Stop the Prolog child. `graceMs` lets the interactive toplevel halt on
127
+ * stdin EOF (running its at_halt hooks) before it is signalled.
128
+ */
129
+ terminate(options?: {
130
+ graceMs?: number;
131
+ }): Promise<void>;
126
132
  }
127
133
  //# sourceMappingURL=prolog.d.ts.map