kibi-cursor 2.7.6 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kibi-cursor",
3
3
  "displayName": "Kibi",
4
- "version": "2.7.6",
4
+ "version": "2.8.1",
5
5
  "description": "Optional Cursor adapter for Kibi. Requires project-local kibi-cli, kibi-mcp, and kibi-core (SWI-Prolog is bundled on Linux and macOS) before the bundled MCP server can start.",
6
6
  "license": "AGPL-3.0-or-later",
7
7
  "author": {
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # kibi-cursor
2
2
 
3
+ ## 2.8.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 5552b1f: `kb_compile_intent` no longer reports "no conflict" for intent it could not check. Prolog compares only requirements that have a logical model, so a claim the advisor left as an ontology gap, or one next to a current requirement that exists only as prose, used to come back as `no_conflict` even when discovery had just returned a requirement saying the opposite. The plan now says the check is unresolved, names the related requirements the agent must compare, and blocks when the agent reports a conflict. Bootstrap plans name the same requirements for review before approval.
8
+ - `contradictionAnalysis` gains `reasons` (`unmodeled_target` with the claim keys, `unmodeled_related` with the requirement ids, `analysis_unavailable`), `reviewCandidates` (up to five related current requirements with discovery score at least 0.1: id, title, the sentence sharing the most terms with the intent, statement, score, `modeled`, `review`) and `nextAction`. Any reason makes `outcome` `unresolved`.
9
+ - New `kb_compile_intent` input `conflictReviews: [{requirementId, verdict: "conflict" | "no_conflict", note?}]`. A `conflict` verdict makes the outcome `conflict` and the plan `blocked`; `no_conflict` clears that candidate. A verdict on a current requirement discovery did not rank is honored; one that names no current requirement is ignored with a diagnostic.
10
+ - Pending reviews do not change the plan status: a plan whose propositions are modeled stays `ready` with `Contradiction check incomplete: …` in its diagnostics and becomes `no_conflict` once every candidate is reviewed. An unmodeled target keeps the plan `needs_resolution` as before, and its outcome stays `unresolved` after review.
11
+ - `kb_plan_bootstrap` adds `review_against:<id>` evidence and a `conflict-review:` diagnostic for each planned intent claim that has prose-only current requirements on the same topic.
12
+ - `kibi-usage` 2.20.0 and `kibi-bootstrap` 3.15.0 tell the agent to compare and report instead of reading `unresolved` as consistent.
13
+
14
+ - Updated dependencies [f4c5822]
15
+ - kibi-agent-core@0.8.1
16
+
17
+ ## 2.8.0
18
+
19
+ ### Minor Changes
20
+
21
+ - 00d2871: The Codex, Cursor and ZCode hooks now act on recorded decisions while the work happens, as the Claude Code hooks do.
22
+ - **Firm requirements before the edit.** When an edit changes code that implements a current firm requirement, the pre-edit hook tells the agent to ask the person before an edit that breaks that rule, naming the rule; once per requirement per session. These hosts cannot put the question to the person from a hook (Codex has no "ask", Cursor does not enforce one, and the ZCode adapter stays advisory), so this is a notice to the agent. Set `KIBI_CONFIRM_FIRM_EDITS=0` to turn it off.
23
+ - **Stated rules are captured.** A prompt that reads as a rule ("must", "never", "from now on") gets the note that recording it with `kb_upsert` is part of the change: through a new `UserPromptSubmit` hook in Codex and ZCode, and in Cursor through a new `beforeSubmitPrompt` hook whose note arrives with the next pre-edit message. When a session changed source files and wrote nothing with `kb_upsert`, the stop hook asks once for the decision to be recorded (Codex continues the turn with it; Cursor adds it to the stop follow-up; ZCode adds it to the Stop context).
24
+ - **Codex unlinked-file candidates** are ranked by the latest prompt's words as well as the path's.
25
+
26
+ `kibi-agent-core`: new `kibi-agent-core/decision-guard` entry with the host-neutral pieces (`firmRequirementsTouched`, `firmEditNotice`, `confirmFirmEdits`, the prompt and stop notes, and per-session markers). `kibi-claude` now imports its firm-edit and capture texts from it; behavior is unchanged.
27
+
28
+ ### Patch Changes
29
+
30
+ - Updated dependencies [00d2871]
31
+ - kibi-agent-core@0.8.0
32
+
3
33
  ## 2.7.6
4
34
 
5
35
  ### Patch Changes
package/README.md CHANGED
@@ -122,11 +122,12 @@ agent-plugin/
122
122
  Hooks are warning-only and never replace MCP/CLI behavior:
123
123
 
124
124
  - **sessionStart**: bootstrap reminder when `.kb/manifest.json` is missing.
125
- - **preToolUse**: warns on explicit direct `.kb/**` edits without blocking. Before the first edit of a file whose symbols implement a requirement, it adds the shared Kibi snippet (from `kibi-agent-core/snippets`, the same builder the Claude Code plugin uses): the requirements and their implementing symbols, what the lead requirement must keep true and the decision behind it, the covering tests, and the symbol the edit lands in. A superseded or retired lead requirement gets no "must keep true" or "Decision" line. Once per path per session.
125
+ - **preToolUse**: warns on explicit direct `.kb/**` edits without blocking. Before the first edit of a file whose symbols implement a requirement, it adds the shared Kibi snippet (from `kibi-agent-core/snippets`, the same builder the Claude Code plugin uses): the requirements and their implementing symbols, what the lead requirement must keep true and the decision behind it, the covering tests, and the symbol the edit lands in. A superseded or retired lead requirement gets no "must keep true" or "Decision" line. Once per path per session. When the edited code implements a firm requirement (a rule the person set), the message first tells the agent to ask the person before an edit that breaks it, once per requirement per conversation (Cursor does not enforce a hook's "ask"; `KIBI_CONFIRM_FIRM_EDITS=0` turns this off).
126
+ - **beforeSubmitPrompt**: never blocks a prompt. A prompt that states a rule ("must not", "from now on") leaves a note, delivered with the next pre-edit message, to record that rule with `kb_upsert` as part of the change.
126
127
  - **beforeReadFile** and **postToolUse (Read)**: inject source-linked lookup guidance once per path per session, with the same snippet minus the "must keep true" and "Decision" lines.
127
128
  - **postToolUse** (opt-in telemetry): with `KIBI_DIAGNOSTIC_MODE=1`, appends `interface: "hook"` rows with `host: "cursor"` to `.kb/usage.log` for `kb_*` lookups (MCP or `kibi <route>` in Shell) and for edited files with the requirements they implement, keyed by `conversation_id`. `kibi usage-metrics` reads them for the `lookup_before_first_edit` metric. Nothing is written without the variable.
128
129
  - **postToolUse (Write/Edit)**: inject traceability and freshness guidance once per path per session, including `kb_check({sourceFiles:[...], includeImpactDiagnostics:true, includeWorkingTreeDiff:true})` for meaningful source edits.
129
- - **stop**: emits a single freshness or impact-check follow-up when meaningful paths were **edited** during the turn (reads and search do not count). Plan delivery (`CreatePlan`) stays silent unless that same turn also edited source or mutated the KB.
130
+ - **stop**: emits a single freshness or impact-check follow-up when meaningful paths were **edited** during the turn (reads and search do not count). Plan delivery (`CreatePlan`) stays silent unless that same turn also edited source or mutated the KB. When the conversation changed source files and recorded nothing with `kb_upsert`, the follow-up also asks, once, for the decision behind the change to be recorded.
130
131
 
131
132
  ### What the plugin does not do
132
133
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "kibi",
4
- "version": "2.7.6",
4
+ "version": "2.8.1",
5
5
  "description": "Repo-local, per-branch, queryable knowledge base. Portable Agent Plugin packaging Kibi's Agent Skills and MCP server.",
6
6
  "author": { "name": "Kibi" },
7
7
  "homepage": "https://looted.github.io/kibi/",
@@ -2,7 +2,7 @@
2
2
  id: kibi-bootstrap
3
3
  name: kibi-bootstrap
4
4
  description: "Use for any Kibi bootstrap or onboarding task: seeding a new or thin knowledge base; reviewing a bootstrap or onboarding plan, preview, or evidence; judging approval readiness or coverage; diagnosing a blocked, partial, or failed bootstrap or its repair; applying an approved bootstrap plan; or asking the human which knowledge sources hold intent. Covers kb_status first, source interview, cited intent claims, read-only kb_plan_bootstrap preview, explicit approval, exact-plan kb_apply_plan, and repair-safe close-out."
5
- version: 3.14.0
5
+ version: 3.15.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -37,7 +37,7 @@ Seed branch-local Kibi knowledge from cited product intent, without creating a p
37
37
  3. **Declare** `bootstrapContext`: `projectSummary`; `knowledgeSources` (id, kind, title, locator, authority, optional connector, optional `component`); `intentClaims` (statement, sourceId, reference, `excerpt` required for `intent` and `observation` claims and optional for `open_question`, optional `kind`, default `intent`, optional `component` overriding the source's, optional `rationale` with the reason the source or the human gave, never invented); `conflicts` (`claimReferences` naming two or more declared claims by sourceId and reference, plus a one-sentence `note`). Only intent claims can become requirements, and they are recorded as the people's rules: `origin.kind: import` with `sourceId:reference` as `origin.ref`, and `firmness: firm`, so a later agent asks before changing one (normative lines from repository Markdown get the same, citing `path#Lline`). Observations, open questions (tagged `review:open-question`) and conflicts (tagged `review:conflict`) become cited observation facts. Generated subject keys take the shape `component.aspect`: name the component each source or claim is about (for example `recorder`; set `component` on the knowledge source, or on a claim to override it; repository Markdown takes its file or directory name), because an intent claim whose subject is neither one word nor already dotted, with no component, is reported in the plan diagnostics and left as an authoring follow-up instead of a malformed key. The aspect is the claim's subject, or its constrained property when the subject is the component itself. An aspect over four words or 40 characters is shortened to a noun phrase (words before `when`, `while`, `for`, `and`, ...; `support_inbox.update_in_real_time_when_a_new_ticket_is_assigned...` becomes `support_inbox.update_in_real_time`) and reported as `subject-key-shortened:` with both keys; two different subjects that shorten alike get a third segment (`editor.discard_a_draft.leaving`) and a `subject-key-disambiguated:` diagnostic; claims about one subject share one subject fact, and a `subject-key-shared:` diagnostic names the key and its sources. Show these diagnostics to the human with the plan so they can rename a key before approval. Kibi never contacts these sources. It binds your declaration into the plan hash.
38
38
  4. **Preview** with `kb_plan_bootstrap` (or `plan-bootstrap --input`), read-only. On `needs_context`, ask only its bounded questions (four at most), then preview again. When `pluginOffers` is non-empty (for example `kibi-plugin-ui` because UI component files exist), ask the human once whether to enable each offered plugin, naming its evidence files. On yes, install it as a dev dependency and add it to `package.json` `kibi.plugins` with the offered capability in `augment` mode; on no, add its name to `kibi.declinedPlugins` so it is not offered again. Either way the human decides; never enable a plugin unasked. For `kibi-plugin-ui`, step 11 also captures the UI pattern inventory (see `kibi-usage` `resources/ui-requirements.md`).
39
39
  5. **Narrow with filters, not caps.** Read the `tldr` and the `Suppressed candidates by reason` diagnostic before individual rows. If the output is too large or mostly tooling or metadata, re-preview with `entityTypes` or declared `sourceOfTruthPaths`. Generic Markdown is off by default once you declare intent claims; set `includeGenericMarkdown: true` only for repository docs the human named. Keep confirmed intent claims: they are never capped and never use `maxCandidates`, which is the separate budget for candidates Kibi discovers (symbols, tests, repository docs). Raise it only when discovered candidates you need are `over_limit`, and never for provenance stubs: a provider entry with no claim (`Source module: …`, repository metadata, layout roots) is planned last as a `meta` fact tagged `bootstrap:provenance-stub`, `discoverySummary.provenanceStubs` counts them apart from `candidatesWithClaims`, and the `over_limit` diagnostic says how many suppressed candidates are stubs; they add no knowledge, so a plan that is mostly stubs needs `entityTypes` or declared sources, not a higher cap. Read the per-source `Knowledge source …` diagnostics: each splits the claims not planned by reason (suppressed by the activation mode, a `suppressedCandidates` row, a `sourceOnlySignals` follow-up). Report them per source and restate them before approval, or carry them to step 11 with their citation.
40
- 6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
40
+ 6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. A `conflict-review:` diagnostic (and `review_against:<id>` evidence on the candidate) names current requirements Kibi could not check a planned claim against because they have no logic: compare each pair yourself and show any contradiction to the human with the plan; they decide whether to drop the claim or keep it and supersede the requirement after apply. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
41
41
  7. **Pre-apply check.** The plan you send must equal the returned plan: every top-level field, including `suppressedCandidates`, `diagnostics`, `candidates`, `actions`, and `expected`, with every array the same length and order. Never rebuild it from preview fields. If the host cannot pass the exact object, stop and say so.
42
42
  8. **Apply once** with `kb_apply_plan`, the exact plan, and `approvedPlanHash`. The operation owns dependency ordering, source-first writes, sequential mutation, and recovery journaling. A plan with hundreds of actions can run for minutes: pass a `progressToken` (Kibi sends progress after each action) with a client that resets its timeout on progress, or pass `async: true` and poll `kb_job_status` when the host has it. If the apply is cut off, call `kb_apply_plan` with only the `recoveryJournalId` from `kb_status` or the error; it resumes an action interrupted mid-write and reclaims a lock left by a dead process. Never edit or delete `.kb/recovery` files; if recovery is refused, stop and report it.
43
43
  9. **Read back** with `kb_query` and `kb_search` to confirm writes and citations.
@@ -2,7 +2,7 @@
2
2
  id: kibi-usage
3
3
  name: Kibi Usage
4
4
  description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
5
- version: 2.19.0
5
+ version: 2.20.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -410,6 +410,16 @@ Conditional clauses ("only when", "only if", "must not ... unless") compile
410
410
  into typed `forbid` rules linked by `requires_rule`, not property facts; one
411
411
  `kb_model` or `kb_compile_intent` cannot translate stays an `ontology_gap`.
412
412
 
413
+ `kb_compile_intent` reports `contradictionAnalysis.outcome: no_conflict` only
414
+ when it could check: Prolog compares only requirements with logic links. When
415
+ the intent is unmodeled or a related current requirement is prose only, the
416
+ outcome is `unresolved` with `reasons` and up to five `reviewCandidates`. Never
417
+ read `unresolved` as consistent. Compare the intent with each candidate's
418
+ `statement`, then compile again with `conflictReviews`
419
+ (`{requirementId, verdict: "conflict" | "no_conflict", note}`). On a conflict
420
+ the plan is `blocked`: ask the human which rule holds (a firm requirement is
421
+ theirs), then supersede or change it, or change the intent.
422
+
413
423
  ## Semantic and proof guardrails
414
424
 
415
425
  Prolog proves only encoded facts. Keep ambiguity, ontology gaps, incomplete
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kibi-cursor",
3
3
  "displayName": "Kibi",
4
- "version": "2.7.6",
4
+ "version": "2.8.1",
5
5
  "description": "Optional Cursor adapter for Kibi. Requires project-local kibi-cli, kibi-mcp, and kibi-core (SWI-Prolog is bundled on Linux and macOS) before the bundled MCP server can start.",
6
6
  "license": "AGPL-3.0-or-later",
7
7
  "author": {
@@ -1,4 +1,4 @@
1
- export type HookEvent = "sessionStart" | "preToolUse" | "postToolUse" | "beforeReadFile" | "stop";
1
+ export type HookEvent = "sessionStart" | "preToolUse" | "postToolUse" | "beforeReadFile" | "beforeSubmitPrompt" | "stop";
2
2
  export type HookStopStatus = "completed" | "aborted" | "error";
3
3
  export type HookInput = {
4
4
  event: string;
@@ -9,6 +9,8 @@ export type HookInput = {
9
9
  conversationId?: string;
10
10
  filePath?: string;
11
11
  status?: HookStopStatus;
12
+ /** beforeSubmitPrompt: the submitted prompt. */
13
+ prompt?: string;
12
14
  };
13
15
  export declare function parseHookInput(input: unknown): HookInput;
14
16
  export declare function readStdin(): Promise<string>;
@@ -68,6 +68,7 @@ export function parseHookInput(input) {
68
68
  ]);
69
69
  const filePath = readString(input, ["file_path", "filePath", "path"]);
70
70
  const status = parseStopStatus(readString(input, ["status"]));
71
+ const prompt = readString(input, ["prompt"]);
71
72
  const parsed = { event };
72
73
  if (cwd !== undefined) {
73
74
  parsed.cwd = cwd;
@@ -87,6 +88,9 @@ export function parseHookInput(input) {
87
88
  if (filePath !== undefined) {
88
89
  parsed.filePath = filePath;
89
90
  }
91
+ if (prompt !== undefined) {
92
+ parsed.prompt = prompt;
93
+ }
90
94
  if (status !== undefined) {
91
95
  parsed.status = status;
92
96
  }
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  export type CursorHookResult = {
3
+ /** beforeSubmitPrompt only: whether the prompt goes through. */
4
+ continue?: boolean;
3
5
  additional_context?: string;
4
6
  permission?: "allow" | "deny";
5
7
  user_message?: string;
@@ -3,6 +3,7 @@
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
+ import { captureReminder, claimCaptureReminder, confirmFirmEdits, firmEditNotice, firmRequirementsTouched, hasDecisionFlag, holdRuleNote, markDecisionFlag, recordRequest, settleFirmRequirements, settledFirmRequirements, takeRuleNote, } from "kibi-agent-core/decision-guard";
6
7
  import { appendHookUsageRows, editTraces, hookTelemetryEnabled, kbUsageTrace, readPackageVersion, } from "kibi-agent-core/hook-usage-log";
7
8
  import { stampKibiWorkspace } from "kibi-agent-core/kb-mcp-tools";
8
9
  import { createEntitySummarizer, editFocus, fileKnowledgeSnippet, implementedRequirementIds, } from "kibi-agent-core/snippets";
@@ -11,7 +12,7 @@ import { parseHookInput, parseStdinJson, readStdin } from "./hook-input.js";
11
12
  import { addDirtyPaths, clearSessionHookState, hasGuidedPath, loadHookState, recordKbMcpTool, recordPlanDelivered, rememberGuidedPath, resolveStateDir, } from "./hook-state.js";
12
13
  import { extractKbMcpToolCall } from "./kb-mcp-tools.js";
13
14
  import { BOOTSTRAP_REMINDER, DIRECT_KB_EDIT_WARNING, interfaceAdvisory, stopFollowupMessage, } from "./messages.js";
14
- import { extractExplicitPathFields, isDirectKbPath, isKbFreshnessRelevantPath, isMeaningfulTrackedPath, toRepoRelativePath, } from "./path-policy.js";
15
+ import { extractExplicitPathFields, isDirectKbPath, isKbFreshnessRelevantPath, isMeaningfulTrackedPath, isSourceImpactRelevantPath, toRepoRelativePath, } from "./path-policy.js";
15
16
  import { getSourceLinkedSymbols, } from "./source-linked-requirements.js";
16
17
  const MAX_LINKED_REQUIREMENTS = 3;
17
18
  /**
@@ -31,6 +32,28 @@ function knowledgeFor(workspaceRoot, linked, surface, focus) {
31
32
  summarize: createEntitySummarizer(workspaceRoot),
32
33
  });
33
34
  }
35
+ // implements REQ-agent-hosts-decision-guard
36
+ /**
37
+ * Cursor does not enforce a hook's "ask", so an edit of code that implements
38
+ * a firm requirement gets a notice telling the agent to ask the person first,
39
+ * once per requirement per conversation.
40
+ */
41
+ function firmNotice(workspaceRoot, linked, focus, session, env) {
42
+ if (!confirmFirmEdits(env))
43
+ return undefined;
44
+ const summarize = createEntitySummarizer(workspaceRoot);
45
+ const { ids, symbol } = firmRequirementsTouched({
46
+ symbols: linked.symbols,
47
+ focus,
48
+ wholeFile: focus === undefined,
49
+ summarize,
50
+ confirmed: settledFirmRequirements(session),
51
+ });
52
+ if (ids.length === 0)
53
+ return undefined;
54
+ settleFirmRequirements(session, ids);
55
+ return firmEditNotice(symbol ? `${symbol.title} in ${linked.relativePath}` : linked.relativePath, ids.map(summarize));
56
+ }
34
57
  let cachedPackageVersion;
35
58
  function packageVersion() {
36
59
  // dist/hook-runner.js sits one level below the package root.
@@ -140,6 +163,10 @@ export async function runHook(rawInput, environment = {}) {
140
163
  const kibiReady = hasKibiConfig(cwd);
141
164
  const workspaceTrusted = environment.workspaceTrusted === true;
142
165
  const initialState = loadHookState(stateDir);
166
+ const session = {
167
+ stateDir,
168
+ sessionId: input.conversationId,
169
+ };
143
170
  switch (input.event) {
144
171
  case "sessionStart":
145
172
  if (!kibiReady) {
@@ -177,23 +204,38 @@ export async function runHook(rawInput, environment = {}) {
177
204
  }
178
205
  const relativePath = toRepoRelativePath(primaryPath, cwd);
179
206
  const state = loadHookState(stateDir);
180
- if (hasGuidedPath(state, "pre-edit", relativePath)) {
181
- return emptyResult();
182
- }
183
207
  const linked = getSourceLinkedSymbols(cwd, primaryPath, stateDir);
184
- const guidance = preEditGuidance(primaryPath, {
185
- cwd,
186
- hasKibi: kibiReady,
187
- mcpState: state.mcpState,
188
- workspaceTrusted,
189
- linkedRequirementIds: implementedRequirementIds(linked.symbols).slice(0, MAX_LINKED_REQUIREMENTS),
190
- knowledge: knowledgeFor(cwd, linked, "edit", editFocus(path.resolve(cwd, primaryPath), input.toolInput)),
191
- });
192
- if (!guidance) {
193
- return emptyResult();
208
+ const focus = editFocus(path.resolve(cwd, primaryPath), input.toolInput);
209
+ const ruleNote = takeRuleNote(session);
210
+ const notice = firmNotice(cwd, linked, focus, session, environment.env ?? process.env);
211
+ const guidance = hasGuidedPath(state, "pre-edit", relativePath)
212
+ ? undefined
213
+ : preEditGuidance(primaryPath, {
214
+ cwd,
215
+ hasKibi: kibiReady,
216
+ mcpState: state.mcpState,
217
+ workspaceTrusted,
218
+ linkedRequirementIds: implementedRequirementIds(linked.symbols).slice(0, MAX_LINKED_REQUIREMENTS),
219
+ knowledge: knowledgeFor(cwd, linked, "edit", focus),
220
+ });
221
+ if (guidance) {
222
+ rememberGuidedPath(stateDir, "pre-edit", relativePath);
223
+ }
224
+ const parts = [ruleNote, notice, guidance].filter((part) => part !== undefined);
225
+ return parts.length > 0
226
+ ? { permission: "allow", agent_message: parts.join("\n\n") }
227
+ : emptyResult();
228
+ }
229
+ // implements REQ-agent-hosts-decision-guard
230
+ case "beforeSubmitPrompt": {
231
+ // Cursor's prompt hook cannot add context: the request's rule note
232
+ // waits for the next pre-edit message.
233
+ if (kibiReady && input.prompt) {
234
+ const rule = recordRequest(session, input.prompt);
235
+ if (rule !== undefined)
236
+ holdRuleNote(session, rule);
194
237
  }
195
- rememberGuidedPath(stateDir, "pre-edit", relativePath);
196
- return { permission: "allow", agent_message: guidance };
238
+ return { continue: true };
197
239
  }
198
240
  case "beforeReadFile": {
199
241
  const primaryPath = resolvePrimaryPath(input);
@@ -249,6 +291,14 @@ export async function runHook(rawInput, environment = {}) {
249
291
  if (dirtyPaths.length > 0) {
250
292
  addDirtyPaths(stateDir, dirtyPaths);
251
293
  }
294
+ if (dirtyPaths.some(isSourceImpactRelevantPath)) {
295
+ markDecisionFlag(session, "source-edited");
296
+ }
297
+ }
298
+ if (kbToolCall?.toolName === "kb_upsert" ||
299
+ kbUsageTrace(input.toolName, input.toolInput)?.kbOperation ===
300
+ "kb_upsert") {
301
+ markDecisionFlag(session, "kb-recorded");
252
302
  }
253
303
  const primaryPath = resolvePrimaryPath(input);
254
304
  if (!primaryPath || !kibiReady) {
@@ -307,7 +357,17 @@ export async function runHook(rawInput, environment = {}) {
307
357
  }
308
358
  return emptyResult();
309
359
  }
310
- const followupMessage = stopFollowupMessage(state);
360
+ // implements REQ-agent-hosts-decision-guard
361
+ // Source changed and nothing was recorded: the follow-up asks for the
362
+ // decision to be recorded, once per conversation.
363
+ const followupMessage = [
364
+ claimCaptureReminder(session)
365
+ ? captureReminder(hasDecisionFlag(session, "rule-prompt"))
366
+ : undefined,
367
+ stopFollowupMessage(state),
368
+ ]
369
+ .filter((part) => part !== undefined)
370
+ .join("\n");
311
371
  if (followupMessage) {
312
372
  clearSessionHookState(stateDir);
313
373
  return { followup_message: followupMessage };
@@ -6,6 +6,11 @@
6
6
  "command": "node dist/hook-runner.js"
7
7
  }
8
8
  ],
9
+ "beforeSubmitPrompt": [
10
+ {
11
+ "command": "node dist/hook-runner.js"
12
+ }
13
+ ],
9
14
  "preToolUse": [
10
15
  {
11
16
  "command": "node dist/hook-runner.js"
@@ -1,5 +1,5 @@
1
1
  {
2
- "kibi-bootstrap/SKILL.md": "81787f3c3936c57e7d7a354c15a197d8dfd9fad5b3920bd655fe9ce91c8bfcc6",
2
+ "kibi-bootstrap/SKILL.md": "fa8fc77f2d53b73345a88f1d22460c5713d55813e721d6fe2ac1cf3abd502a97",
3
3
  "kibi-bootstrap/resources/bootstrap.md": "e93fae88a6853d0092cde7eee33f7d4943a8141e3841bfe3f64ab73247867b6a",
4
4
  "kibi-bootstrap/resources/branch-lifecycle.md": "c854264ce888439140883a3fc636db5e522f020dd27b30a5c1ecc4105b64ed13",
5
5
  "kibi-bootstrap/resources/operation-access.md": "b5a6a59e72d800c376e7fad79132897623c32a0ce152a1e8d73f9628b444db51",
@@ -13,7 +13,7 @@
13
13
  "kibi-traceability/resources/operation-access.md": "39f7972ab7f4ae7479e78f7072bb50e8e6c4cdf4f857425f69d2522d37db437c",
14
14
  "kibi-traceability/resources/source-authoring.md": "408c6ee2bef1b84e0194c0538ef929c26a0a617e631b2234247388140dbbdd08",
15
15
  "kibi-traceability/resources/traceability.md": "38c290a0bc71bd1692e115f22694743946e2f69630e134acfd43cee5f613fb88",
16
- "kibi-usage/SKILL.md": "1d3ac2064f5c65d702db7445d36d8bc0ed741355d7a25ad301ab326e201d8e76",
16
+ "kibi-usage/SKILL.md": "82cfdea4088ab218722a11e325ce6dc558e53d11644a25155b82871bea60d8f7",
17
17
  "kibi-usage/resources/branch-lifecycle.md": "f1bf359e9826e83c548f3cd0287f8175a62456527b0132b06b3f9f74aa59b7d6",
18
18
  "kibi-usage/resources/fact-lanes.md": "899218f90396aa469b6e34aeb5f7ea1f38bf11bb326102319f376c213859fb33",
19
19
  "kibi-usage/resources/kb-improvement.md": "a8408fce27342bdce6415a6718599e53fe00bb33aa1fd3dc42353ec6f61adcd7",
@@ -2,7 +2,7 @@
2
2
  id: kibi-bootstrap
3
3
  name: kibi-bootstrap
4
4
  description: "Use for any Kibi bootstrap or onboarding task: seeding a new or thin knowledge base; reviewing a bootstrap or onboarding plan, preview, or evidence; judging approval readiness or coverage; diagnosing a blocked, partial, or failed bootstrap or its repair; applying an approved bootstrap plan; or asking the human which knowledge sources hold intent. Covers kb_status first, source interview, cited intent claims, read-only kb_plan_bootstrap preview, explicit approval, exact-plan kb_apply_plan, and repair-safe close-out."
5
- version: 3.14.0
5
+ version: 3.15.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -37,7 +37,7 @@ Seed branch-local Kibi knowledge from cited product intent, without creating a p
37
37
  3. **Declare** `bootstrapContext`: `projectSummary`; `knowledgeSources` (id, kind, title, locator, authority, optional connector, optional `component`); `intentClaims` (statement, sourceId, reference, `excerpt` required for `intent` and `observation` claims and optional for `open_question`, optional `kind`, default `intent`, optional `component` overriding the source's, optional `rationale` with the reason the source or the human gave, never invented); `conflicts` (`claimReferences` naming two or more declared claims by sourceId and reference, plus a one-sentence `note`). Only intent claims can become requirements, and they are recorded as the people's rules: `origin.kind: import` with `sourceId:reference` as `origin.ref`, and `firmness: firm`, so a later agent asks before changing one (normative lines from repository Markdown get the same, citing `path#Lline`). Observations, open questions (tagged `review:open-question`) and conflicts (tagged `review:conflict`) become cited observation facts. Generated subject keys take the shape `component.aspect`: name the component each source or claim is about (for example `recorder`; set `component` on the knowledge source, or on a claim to override it; repository Markdown takes its file or directory name), because an intent claim whose subject is neither one word nor already dotted, with no component, is reported in the plan diagnostics and left as an authoring follow-up instead of a malformed key. The aspect is the claim's subject, or its constrained property when the subject is the component itself. An aspect over four words or 40 characters is shortened to a noun phrase (words before `when`, `while`, `for`, `and`, ...; `support_inbox.update_in_real_time_when_a_new_ticket_is_assigned...` becomes `support_inbox.update_in_real_time`) and reported as `subject-key-shortened:` with both keys; two different subjects that shorten alike get a third segment (`editor.discard_a_draft.leaving`) and a `subject-key-disambiguated:` diagnostic; claims about one subject share one subject fact, and a `subject-key-shared:` diagnostic names the key and its sources. Show these diagnostics to the human with the plan so they can rename a key before approval. Kibi never contacts these sources. It binds your declaration into the plan hash.
38
38
  4. **Preview** with `kb_plan_bootstrap` (or `plan-bootstrap --input`), read-only. On `needs_context`, ask only its bounded questions (four at most), then preview again. When `pluginOffers` is non-empty (for example `kibi-plugin-ui` because UI component files exist), ask the human once whether to enable each offered plugin, naming its evidence files. On yes, install it as a dev dependency and add it to `package.json` `kibi.plugins` with the offered capability in `augment` mode; on no, add its name to `kibi.declinedPlugins` so it is not offered again. Either way the human decides; never enable a plugin unasked. For `kibi-plugin-ui`, step 11 also captures the UI pattern inventory (see `kibi-usage` `resources/ui-requirements.md`).
39
39
  5. **Narrow with filters, not caps.** Read the `tldr` and the `Suppressed candidates by reason` diagnostic before individual rows. If the output is too large or mostly tooling or metadata, re-preview with `entityTypes` or declared `sourceOfTruthPaths`. Generic Markdown is off by default once you declare intent claims; set `includeGenericMarkdown: true` only for repository docs the human named. Keep confirmed intent claims: they are never capped and never use `maxCandidates`, which is the separate budget for candidates Kibi discovers (symbols, tests, repository docs). Raise it only when discovered candidates you need are `over_limit`, and never for provenance stubs: a provider entry with no claim (`Source module: …`, repository metadata, layout roots) is planned last as a `meta` fact tagged `bootstrap:provenance-stub`, `discoverySummary.provenanceStubs` counts them apart from `candidatesWithClaims`, and the `over_limit` diagnostic says how many suppressed candidates are stubs; they add no knowledge, so a plan that is mostly stubs needs `entityTypes` or declared sources, not a higher cap. Read the per-source `Knowledge source …` diagnostics: each splits the claims not planned by reason (suppressed by the activation mode, a `suppressedCandidates` row, a `sourceOnlySignals` follow-up). Report them per source and restate them before approval, or carry them to step 11 with their citation.
40
- 6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
40
+ 6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. A `conflict-review:` diagnostic (and `review_against:<id>` evidence on the candidate) names current requirements Kibi could not check a planned claim against because they have no logic: compare each pair yourself and show any contradiction to the human with the plan; they decide whether to drop the claim or keep it and supersede the requirement after apply. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
41
41
  7. **Pre-apply check.** The plan you send must equal the returned plan: every top-level field, including `suppressedCandidates`, `diagnostics`, `candidates`, `actions`, and `expected`, with every array the same length and order. Never rebuild it from preview fields. If the host cannot pass the exact object, stop and say so.
42
42
  8. **Apply once** with `kb_apply_plan`, the exact plan, and `approvedPlanHash`. The operation owns dependency ordering, source-first writes, sequential mutation, and recovery journaling. A plan with hundreds of actions can run for minutes: pass a `progressToken` (Kibi sends progress after each action) with a client that resets its timeout on progress, or pass `async: true` and poll `kb_job_status` when the host has it. If the apply is cut off, call `kb_apply_plan` with only the `recoveryJournalId` from `kb_status` or the error; it resumes an action interrupted mid-write and reclaims a lock left by a dead process. Never edit or delete `.kb/recovery` files; if recovery is refused, stop and report it.
43
43
  9. **Read back** with `kb_query` and `kb_search` to confirm writes and citations.
@@ -2,7 +2,7 @@
2
2
  id: kibi-usage
3
3
  name: Kibi Usage
4
4
  description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
5
- version: 2.19.0
5
+ version: 2.20.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -410,6 +410,16 @@ Conditional clauses ("only when", "only if", "must not ... unless") compile
410
410
  into typed `forbid` rules linked by `requires_rule`, not property facts; one
411
411
  `kb_model` or `kb_compile_intent` cannot translate stays an `ontology_gap`.
412
412
 
413
+ `kb_compile_intent` reports `contradictionAnalysis.outcome: no_conflict` only
414
+ when it could check: Prolog compares only requirements with logic links. When
415
+ the intent is unmodeled or a related current requirement is prose only, the
416
+ outcome is `unresolved` with `reasons` and up to five `reviewCandidates`. Never
417
+ read `unresolved` as consistent. Compare the intent with each candidate's
418
+ `statement`, then compile again with `conflictReviews`
419
+ (`{requirementId, verdict: "conflict" | "no_conflict", note}`). On a conflict
420
+ the plan is `blocked`: ask the human which rule holds (a firm requirement is
421
+ theirs), then supersede or change it, or change the intent.
422
+
413
423
  ## Semantic and proof guardrails
414
424
 
415
425
  Prolog proves only encoded facts. Keep ambiguity, ontology gaps, incomplete
package/hooks/hooks.json CHANGED
@@ -6,6 +6,11 @@
6
6
  "command": "node dist/hook-runner.js"
7
7
  }
8
8
  ],
9
+ "beforeSubmitPrompt": [
10
+ {
11
+ "command": "node dist/hook-runner.js"
12
+ }
13
+ ],
9
14
  "preToolUse": [
10
15
  {
11
16
  "command": "node dist/hook-runner.js"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cursor",
3
- "version": "2.7.6",
3
+ "version": "2.8.1",
4
4
  "description": "Kibi Cursor plugin - thin adapter for Cursor MCP integration and editor hooks",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -59,7 +59,7 @@
59
59
  }
60
60
  },
61
61
  "dependencies": {
62
- "kibi-agent-core": "^0.7.0"
62
+ "kibi-agent-core": "^0.8.1"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/node": "latest",
@@ -1,5 +1,5 @@
1
1
  {
2
- "kibi-bootstrap/SKILL.md": "81787f3c3936c57e7d7a354c15a197d8dfd9fad5b3920bd655fe9ce91c8bfcc6",
2
+ "kibi-bootstrap/SKILL.md": "fa8fc77f2d53b73345a88f1d22460c5713d55813e721d6fe2ac1cf3abd502a97",
3
3
  "kibi-bootstrap/resources/bootstrap.md": "e93fae88a6853d0092cde7eee33f7d4943a8141e3841bfe3f64ab73247867b6a",
4
4
  "kibi-bootstrap/resources/branch-lifecycle.md": "c854264ce888439140883a3fc636db5e522f020dd27b30a5c1ecc4105b64ed13",
5
5
  "kibi-bootstrap/resources/operation-access.md": "b5a6a59e72d800c376e7fad79132897623c32a0ce152a1e8d73f9628b444db51",
@@ -13,7 +13,7 @@
13
13
  "kibi-traceability/resources/operation-access.md": "39f7972ab7f4ae7479e78f7072bb50e8e6c4cdf4f857425f69d2522d37db437c",
14
14
  "kibi-traceability/resources/source-authoring.md": "408c6ee2bef1b84e0194c0538ef929c26a0a617e631b2234247388140dbbdd08",
15
15
  "kibi-traceability/resources/traceability.md": "38c290a0bc71bd1692e115f22694743946e2f69630e134acfd43cee5f613fb88",
16
- "kibi-usage/SKILL.md": "1d3ac2064f5c65d702db7445d36d8bc0ed741355d7a25ad301ab326e201d8e76",
16
+ "kibi-usage/SKILL.md": "82cfdea4088ab218722a11e325ce6dc558e53d11644a25155b82871bea60d8f7",
17
17
  "kibi-usage/resources/branch-lifecycle.md": "f1bf359e9826e83c548f3cd0287f8175a62456527b0132b06b3f9f74aa59b7d6",
18
18
  "kibi-usage/resources/fact-lanes.md": "899218f90396aa469b6e34aeb5f7ea1f38bf11bb326102319f376c213859fb33",
19
19
  "kibi-usage/resources/kb-improvement.md": "a8408fce27342bdce6415a6718599e53fe00bb33aa1fd3dc42353ec6f61adcd7",
@@ -2,7 +2,7 @@
2
2
  id: kibi-bootstrap
3
3
  name: kibi-bootstrap
4
4
  description: "Use for any Kibi bootstrap or onboarding task: seeding a new or thin knowledge base; reviewing a bootstrap or onboarding plan, preview, or evidence; judging approval readiness or coverage; diagnosing a blocked, partial, or failed bootstrap or its repair; applying an approved bootstrap plan; or asking the human which knowledge sources hold intent. Covers kb_status first, source interview, cited intent claims, read-only kb_plan_bootstrap preview, explicit approval, exact-plan kb_apply_plan, and repair-safe close-out."
5
- version: 3.14.0
5
+ version: 3.15.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -37,7 +37,7 @@ Seed branch-local Kibi knowledge from cited product intent, without creating a p
37
37
  3. **Declare** `bootstrapContext`: `projectSummary`; `knowledgeSources` (id, kind, title, locator, authority, optional connector, optional `component`); `intentClaims` (statement, sourceId, reference, `excerpt` required for `intent` and `observation` claims and optional for `open_question`, optional `kind`, default `intent`, optional `component` overriding the source's, optional `rationale` with the reason the source or the human gave, never invented); `conflicts` (`claimReferences` naming two or more declared claims by sourceId and reference, plus a one-sentence `note`). Only intent claims can become requirements, and they are recorded as the people's rules: `origin.kind: import` with `sourceId:reference` as `origin.ref`, and `firmness: firm`, so a later agent asks before changing one (normative lines from repository Markdown get the same, citing `path#Lline`). Observations, open questions (tagged `review:open-question`) and conflicts (tagged `review:conflict`) become cited observation facts. Generated subject keys take the shape `component.aspect`: name the component each source or claim is about (for example `recorder`; set `component` on the knowledge source, or on a claim to override it; repository Markdown takes its file or directory name), because an intent claim whose subject is neither one word nor already dotted, with no component, is reported in the plan diagnostics and left as an authoring follow-up instead of a malformed key. The aspect is the claim's subject, or its constrained property when the subject is the component itself. An aspect over four words or 40 characters is shortened to a noun phrase (words before `when`, `while`, `for`, `and`, ...; `support_inbox.update_in_real_time_when_a_new_ticket_is_assigned...` becomes `support_inbox.update_in_real_time`) and reported as `subject-key-shortened:` with both keys; two different subjects that shorten alike get a third segment (`editor.discard_a_draft.leaving`) and a `subject-key-disambiguated:` diagnostic; claims about one subject share one subject fact, and a `subject-key-shared:` diagnostic names the key and its sources. Show these diagnostics to the human with the plan so they can rename a key before approval. Kibi never contacts these sources. It binds your declaration into the plan hash.
38
38
  4. **Preview** with `kb_plan_bootstrap` (or `plan-bootstrap --input`), read-only. On `needs_context`, ask only its bounded questions (four at most), then preview again. When `pluginOffers` is non-empty (for example `kibi-plugin-ui` because UI component files exist), ask the human once whether to enable each offered plugin, naming its evidence files. On yes, install it as a dev dependency and add it to `package.json` `kibi.plugins` with the offered capability in `augment` mode; on no, add its name to `kibi.declinedPlugins` so it is not offered again. Either way the human decides; never enable a plugin unasked. For `kibi-plugin-ui`, step 11 also captures the UI pattern inventory (see `kibi-usage` `resources/ui-requirements.md`).
39
39
  5. **Narrow with filters, not caps.** Read the `tldr` and the `Suppressed candidates by reason` diagnostic before individual rows. If the output is too large or mostly tooling or metadata, re-preview with `entityTypes` or declared `sourceOfTruthPaths`. Generic Markdown is off by default once you declare intent claims; set `includeGenericMarkdown: true` only for repository docs the human named. Keep confirmed intent claims: they are never capped and never use `maxCandidates`, which is the separate budget for candidates Kibi discovers (symbols, tests, repository docs). Raise it only when discovered candidates you need are `over_limit`, and never for provenance stubs: a provider entry with no claim (`Source module: …`, repository metadata, layout roots) is planned last as a `meta` fact tagged `bootstrap:provenance-stub`, `discoverySummary.provenanceStubs` counts them apart from `candidatesWithClaims`, and the `over_limit` diagnostic says how many suppressed candidates are stubs; they add no knowledge, so a plan that is mostly stubs needs `entityTypes` or declared sources, not a higher cap. Read the per-source `Knowledge source …` diagnostics: each splits the claims not planned by reason (suppressed by the activation mode, a `suppressedCandidates` row, a `sourceOnlySignals` follow-up). Report them per source and restate them before approval, or carry them to step 11 with their citation.
40
- 6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
40
+ 6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. A `conflict-review:` diagnostic (and `review_against:<id>` evidence on the candidate) names current requirements Kibi could not check a planned claim against because they have no logic: compare each pair yourself and show any contradiction to the human with the plan; they decide whether to drop the claim or keep it and supersede the requirement after apply. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
41
41
  7. **Pre-apply check.** The plan you send must equal the returned plan: every top-level field, including `suppressedCandidates`, `diagnostics`, `candidates`, `actions`, and `expected`, with every array the same length and order. Never rebuild it from preview fields. If the host cannot pass the exact object, stop and say so.
42
42
  8. **Apply once** with `kb_apply_plan`, the exact plan, and `approvedPlanHash`. The operation owns dependency ordering, source-first writes, sequential mutation, and recovery journaling. A plan with hundreds of actions can run for minutes: pass a `progressToken` (Kibi sends progress after each action) with a client that resets its timeout on progress, or pass `async: true` and poll `kb_job_status` when the host has it. If the apply is cut off, call `kb_apply_plan` with only the `recoveryJournalId` from `kb_status` or the error; it resumes an action interrupted mid-write and reclaims a lock left by a dead process. Never edit or delete `.kb/recovery` files; if recovery is refused, stop and report it.
43
43
  9. **Read back** with `kb_query` and `kb_search` to confirm writes and citations.
@@ -2,7 +2,7 @@
2
2
  id: kibi-usage
3
3
  name: Kibi Usage
4
4
  description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
5
- version: 2.19.0
5
+ version: 2.20.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -410,6 +410,16 @@ Conditional clauses ("only when", "only if", "must not ... unless") compile
410
410
  into typed `forbid` rules linked by `requires_rule`, not property facts; one
411
411
  `kb_model` or `kb_compile_intent` cannot translate stays an `ontology_gap`.
412
412
 
413
+ `kb_compile_intent` reports `contradictionAnalysis.outcome: no_conflict` only
414
+ when it could check: Prolog compares only requirements with logic links. When
415
+ the intent is unmodeled or a related current requirement is prose only, the
416
+ outcome is `unresolved` with `reasons` and up to five `reviewCandidates`. Never
417
+ read `unresolved` as consistent. Compare the intent with each candidate's
418
+ `statement`, then compile again with `conflictReviews`
419
+ (`{requirementId, verdict: "conflict" | "no_conflict", note}`). On a conflict
420
+ the plan is `blocked`: ask the human which rule holds (a firm requirement is
421
+ theirs), then supersede or change it, or change the intent.
422
+
413
423
  ## Semantic and proof guardrails
414
424
 
415
425
  Prolog proves only encoded facts. Keep ambiguity, ontology gaps, incomplete