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.
- package/dist/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/cli-register-proof.js +9 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -6
- package/dist/commands/sync/manifest.d.ts +10 -0
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +27 -0
- package/dist/commands/sync/untracked-targets.d.ts +17 -0
- package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
- package/dist/commands/sync/untracked-targets.js +81 -9
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +76 -6
- package/dist/engine.d.ts +4 -0
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +56 -33
- package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applicability.js +5 -2
- package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-catalog-4.js +4 -0
- package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-loader.js +3 -0
- package/dist/operations/modeling/predicate-score-3.js +1 -1
- package/dist/operations/modeling/predicate-types.d.ts +5 -0
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.d.ts +9 -0
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +30 -1
- package/dist/operations/planning/apply-plan.d.ts.map +1 -1
- package/dist/operations/planning/apply-plan.js +10 -1
- package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/clauses.js +7 -1
- package/dist/prolog/store-lock.d.ts +41 -0
- package/dist/prolog/store-lock.d.ts.map +1 -1
- package/dist/prolog/store-lock.js +91 -1
- package/dist/prolog.d.ts +7 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +21 -3
- package/dist/proof/inspect.d.ts +8 -1
- package/dist/proof/inspect.d.ts.map +1 -1
- package/dist/proof/inspect.js +19 -2
- package/dist/public/operations/catalog.d.ts +50 -1
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +4 -4
- package/dist/public/operations/discovery-executors.d.ts +50 -1
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +418 -8
- package/dist/public/operations/migration-plan.d.ts +2 -0
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +31 -0
- package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
- package/dist/public/operations/source-relationship-parity.js +55 -1
- package/dist/public/operations/specs/discovery.d.ts +50 -1
- package/dist/public/operations/specs/discovery.d.ts.map +1 -1
- package/dist/public/operations/specs/discovery.js +56 -1
- package/dist/public/skills/kibi-usage/SKILL.md +20 -3
- package/dist/public/skills/kibi-usage/resources/proof.md +23 -0
- package/dist/runtime/cli-runtime.d.ts.map +1 -1
- package/dist/runtime/cli-runtime.js +7 -2
- package/dist/search-answer.d.ts +12 -3
- package/dist/search-answer.d.ts.map +1 -1
- package/dist/search-answer.js +51 -4
- package/dist/utils/branch-store.d.ts +8 -0
- package/dist/utils/branch-store.d.ts.map +1 -1
- package/dist/utils/branch-store.js +27 -1
- package/dist/utils/content-freshness.d.ts +13 -0
- package/dist/utils/content-freshness.d.ts.map +1 -0
- package/dist/utils/content-freshness.js +171 -0
- package/dist/utils/symbol-manifest-layout.d.ts +9 -0
- package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
- package/dist/utils/symbol-manifest-layout.js +18 -0
- package/package.json +2 -2
- package/src/public/operations/contracts.ts +7 -4
- package/src/public/operations/discovery-executors.ts +556 -6
- package/src/public/operations/migration-plan.ts +44 -0
- package/src/public/operations/source-relationship-parity.ts +60 -3
- package/src/public/operations/specs/discovery.ts +67 -1
- package/src/public/skills/kibi-usage/SKILL.md +20 -3
- 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
|
-
|
|
636
|
-
|
|
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
|
-
|
|
650
|
-
return;
|
|
647
|
+
rmSync(lockPath, { force: true });
|
|
651
648
|
}
|
|
652
649
|
catch {
|
|
653
|
-
|
|
654
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
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
|
-
},
|
|
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 (
|
|
2125
|
-
|
|
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
|
-
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
|
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":"
|
|
1
|
+
{"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -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 ||
|
|
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":"
|
|
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;
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|