kibi-cli 2.19.0 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/cli-operation-metadata.d.ts +1 -1
  2. package/dist/cli-operation-metadata.js +1 -1
  3. package/dist/cli-register-maintenance.js +1 -1
  4. package/dist/commands/engine.d.ts +2 -1
  5. package/dist/commands/engine.d.ts.map +1 -1
  6. package/dist/commands/engine.js +6 -1
  7. package/dist/commands/migrate.d.ts +2 -0
  8. package/dist/commands/migrate.d.ts.map +1 -1
  9. package/dist/commands/migrate.js +32 -6
  10. package/dist/commands/sync/discovery.d.ts.map +1 -1
  11. package/dist/commands/sync/discovery.js +14 -2
  12. package/dist/engine.d.ts.map +1 -1
  13. package/dist/engine.js +24 -6
  14. package/dist/extractors/markdown.d.ts.map +1 -1
  15. package/dist/extractors/markdown.js +2 -1
  16. package/dist/intent-search.d.ts +7 -0
  17. package/dist/intent-search.d.ts.map +1 -1
  18. package/dist/intent-search.js +66 -4
  19. package/dist/operations/migration/kb-sources.d.ts +5 -1
  20. package/dist/operations/migration/kb-sources.d.ts.map +1 -1
  21. package/dist/operations/migration/kb-sources.js +28 -1
  22. package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
  23. package/dist/operations/modeling/predicate-applicability.js +10 -4
  24. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  25. package/dist/operations/modeling/predicate-applyplan.js +16 -5
  26. package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
  27. package/dist/operations/modeling/predicate-catalog-4.js +3 -0
  28. package/dist/operations/modeling/predicate-inference-1.d.ts.map +1 -1
  29. package/dist/operations/modeling/predicate-inference-1.js +34 -7
  30. package/dist/operations/modeling/predicate-score-3.js +1 -1
  31. package/dist/operations/modeling/predicate-types.d.ts +6 -0
  32. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  33. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  34. package/dist/operations/modeling/suggest-predicates.js +111 -0
  35. package/dist/operations/mutation/source-authoring.d.ts +8 -0
  36. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  37. package/dist/operations/mutation/source-authoring.js +33 -0
  38. package/dist/operations/mutation/types.d.ts +26 -0
  39. package/dist/operations/mutation/types.d.ts.map +1 -1
  40. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  41. package/dist/operations/mutation/upsert.js +8 -2
  42. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  43. package/dist/operations/mutation/validate-upsert.js +116 -4
  44. package/dist/operations/mutation/validation.d.ts.map +1 -1
  45. package/dist/operations/mutation/validation.js +2 -1
  46. package/dist/operations/mutation/warnings.d.ts +10 -0
  47. package/dist/operations/mutation/warnings.d.ts.map +1 -1
  48. package/dist/operations/mutation/warnings.js +26 -0
  49. package/dist/operations/mutation/what-if.d.ts +25 -0
  50. package/dist/operations/mutation/what-if.d.ts.map +1 -0
  51. package/dist/operations/mutation/what-if.js +59 -0
  52. package/dist/operations/planning/compile-intent.d.ts +15 -20
  53. package/dist/operations/planning/compile-intent.d.ts.map +1 -1
  54. package/dist/operations/planning/compile-intent.js +64 -53
  55. package/dist/operations/semantic-advisor/clauses.d.ts +9 -0
  56. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  57. package/dist/operations/semantic-advisor/clauses.js +32 -7
  58. package/dist/prolog/janitor.d.ts +26 -2
  59. package/dist/prolog/janitor.d.ts.map +1 -1
  60. package/dist/prolog/janitor.js +55 -3
  61. package/dist/prolog/store-lock.d.ts +28 -3
  62. package/dist/prolog/store-lock.d.ts.map +1 -1
  63. package/dist/prolog/store-lock.js +103 -12
  64. package/dist/public/operations/catalog.d.ts +49 -5
  65. package/dist/public/operations/catalog.d.ts.map +1 -1
  66. package/dist/public/operations/check-executor.d.ts +2 -2
  67. package/dist/public/operations/check-executor.d.ts.map +1 -1
  68. package/dist/public/operations/check-executor.js +35 -0
  69. package/dist/public/operations/check-format-shared.d.ts +16 -0
  70. package/dist/public/operations/check-format-shared.d.ts.map +1 -1
  71. package/dist/public/operations/check-format-shared.js +19 -0
  72. package/dist/public/operations/contracts.d.ts.map +1 -1
  73. package/dist/public/operations/contracts.js +10 -0
  74. package/dist/public/operations/discovery-executors.d.ts +3 -0
  75. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  76. package/dist/public/operations/discovery-executors.js +7 -2
  77. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  78. package/dist/public/operations/migration-plan.js +37 -7
  79. package/dist/public/operations/specs/composite.d.ts +1 -1
  80. package/dist/public/operations/specs/composite.js +1 -1
  81. package/dist/public/operations/specs/discovery.d.ts +2 -2
  82. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  83. package/dist/public/operations/specs/discovery.js +2 -2
  84. package/dist/public/operations/specs/mutation-schemas.d.ts +2 -2
  85. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  86. package/dist/public/operations/specs/mutation-schemas.js +8 -17
  87. package/dist/public/operations/specs/upsert.d.ts +46 -2
  88. package/dist/public/operations/specs/upsert.d.ts.map +1 -1
  89. package/dist/public/operations/specs/upsert.js +14 -0
  90. package/dist/public/schemas/entity.d.ts.map +1 -1
  91. package/dist/public/schemas/entity.js +2 -13
  92. package/dist/public/skills/kibi-usage/SKILL.md +38 -1
  93. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +5 -3
  94. package/dist/public/skills/kibi-usage/resources/ui-requirements.md +2 -2
  95. package/dist/public/skills/kibi-usage/resources/workflows.md +1 -1
  96. package/dist/schemas/entity.schema.json +2 -25
  97. package/dist/search-answer.d.ts +13 -0
  98. package/dist/search-answer.d.ts.map +1 -1
  99. package/dist/search-answer.js +45 -4
  100. package/dist/utils/branch-store.d.ts.map +1 -1
  101. package/dist/utils/branch-store.js +4 -2
  102. package/package.json +3 -3
  103. package/src/public/operations/check-executor.ts +44 -1
  104. package/src/public/operations/check-format-shared.ts +35 -1
  105. package/src/public/operations/contracts.ts +11 -0
  106. package/src/public/operations/discovery-executors.ts +12 -2
  107. package/src/public/operations/migration-plan.ts +41 -9
  108. package/src/public/operations/specs/composite.ts +1 -1
  109. package/src/public/operations/specs/discovery.ts +3 -2
  110. package/src/public/operations/specs/mutation-schemas.ts +8 -17
  111. package/src/public/operations/specs/upsert.ts +15 -0
  112. package/src/public/schemas/entity.ts +2 -13
  113. package/src/public/skills/kibi-usage/SKILL.md +38 -1
  114. package/src/public/skills/kibi-usage/resources/fact-lanes.md +5 -3
  115. package/src/public/skills/kibi-usage/resources/ui-requirements.md +2 -2
  116. package/src/public/skills/kibi-usage/resources/workflows.md +1 -1
  117. package/src/schemas/entity.schema.json +2 -25
@@ -151,7 +151,10 @@ export type SearchInput = {
151
151
 
152
152
  export type SearchPayload = {
153
153
  readonly results: readonly (SearchMatch | IntentSearchMatch)[];
154
+ /** Matches in the whole ranking, before `limit` and `offset`. */
154
155
  readonly count: number;
156
+ /** Rows in `results`: the page this call returned. */
157
+ readonly returned: number;
155
158
  readonly truncated?: boolean;
156
159
  readonly queryAnalysis?: IntentSearchAnalysis;
157
160
  readonly answer?: SearchAnswer;
@@ -772,7 +775,7 @@ export async function executeSearch(
772
775
  const {
773
776
  query,
774
777
  type,
775
- limit = 20,
778
+ limit = 10,
776
779
  offset = 0,
777
780
  rankingMode = "intent-v1",
778
781
  semanticFacets,
@@ -829,6 +832,11 @@ export async function executeSearch(
829
832
  : undefined;
830
833
  // The structured payload carries every row; the text is a short
831
834
  // human-readable digest instead of a second copy of the results.
835
+ // A top match no requirement owns is said in the digest too, so the
836
+ // governing list is never read as the top match's requirement.
837
+ const topUngoverned = answerLayer?.ungoverned.find(
838
+ (entry) => entry.rank === 1,
839
+ );
832
840
  const text = `${
833
841
  intentResult.matches.length === 0
834
842
  ? `No intent search results for '${trimmedQuery}' (abstained).`
@@ -837,13 +845,14 @@ export async function executeSearch(
837
845
  .map((match) => String(match.entity.id ?? ""))
838
846
  .join(
839
847
  ", ",
840
- )}${answerLayer ? `. Governing: ${answerLayer.governing.map((req) => req.id).join(", ") || "none found"}` : ""}.`
848
+ )}${answerLayer ? `. Governing: ${answerLayer.governing.map((req) => req.id).join(", ") || "none found"}` : ""}${topUngoverned ? ` (the top match ${topUngoverned.id} is linked to no requirement)` : ""}.`
841
849
  }${storeNote}`;
842
850
  return {
843
851
  content: [{ type: "text", text }],
844
852
  structuredContent: {
845
853
  results: await projectMatches(prolog, paginated, fields),
846
854
  count: intentResult.matches.length,
855
+ returned: paginated.length,
847
856
  truncated: offset + paginated.length < intentResult.matches.length,
848
857
  queryAnalysis: intentResult.analysis,
849
858
  ...(answerLayer ? { answer: answerLayer } : {}),
@@ -883,6 +892,7 @@ export async function executeSearch(
883
892
  structuredContent: {
884
893
  results: await projectMatches(prolog, paginated, fields),
885
894
  count: matches.length,
895
+ returned: paginated.length,
886
896
  truncated: offset + paginated.length < matches.length,
887
897
  ...branchFields,
888
898
  },
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import * as path from "node:path";
4
+ import { planLegacyStorageMigration } from "../../commands/legacy-storage-migration.js";
4
5
  import { BRANCH_STORE_NOT_COMPILED } from "../../utils/branch-store.js";
5
6
  import { legacyConfigExists } from "../../utils/config.js";
6
7
  import { MTIME_SOURCE_REASON_CODES } from "../../utils/content-freshness.js";
@@ -688,6 +689,14 @@ export function buildActionsFromStatus(input: {
688
689
  const legacyStorageMigrationRequired = legacyConfigExists(
689
690
  input.workspaceRoot,
690
691
  );
692
+ // A legacy cutover that cannot move its files (a destination already
693
+ // exists, a malformed config) is an operator decision, not an automatic
694
+ // step that fails when applied and takes the compile with it.
695
+ const legacyStorage = legacyStorageMigrationRequired
696
+ ? planLegacyStorageMigration(input.workspaceRoot)
697
+ : null;
698
+ const legacyStorageBlocked =
699
+ legacyStorage !== null && legacyStorage.blockers.length > 0;
691
700
  if (storeState === "missing") {
692
701
  actions.push(
693
702
  migrationAction({
@@ -724,6 +733,8 @@ export function buildActionsFromStatus(input: {
724
733
  }
725
734
 
726
735
  if (configStatus.needsMigration || legacyStorageMigrationRequired) {
736
+ const operatorOnly =
737
+ configStatus.status === "newer" || legacyStorageBlocked;
727
738
  actions.push(
728
739
  migrationAction({
729
740
  id: "schema-config-upgrade",
@@ -734,17 +745,34 @@ export function buildActionsFromStatus(input: {
734
745
  ? "legacy_storage_migration"
735
746
  : "schema_version_upgrade",
736
747
  category: "schema",
737
- state: configStatus.status === "newer" ? "blocked" : "ready",
738
- safety: configStatus.status === "newer" ? "operator" : "automatic",
739
- invocation: { kind: "cli", command_argv: ["kibi", "migrate", "--yes"] },
748
+ state: operatorOnly ? "blocked" : "ready",
749
+ safety: operatorOnly ? "operator" : "automatic",
750
+ invocation: legacyStorageBlocked
751
+ ? {
752
+ kind: "review",
753
+ instruction: `The legacy .kb/config.json still names knowledge files outside .kb/ and Kibi cannot move them: ${legacyStorage?.blockers.join(" ")} Keep one copy of each conflicting file (the .kb/ one is what Kibi compiles), remove or git-rm the legacy copy, then run 'kibi migrate --yes' to retire .kb/config.json.`,
754
+ }
755
+ : { kind: "cli", command_argv: ["kibi", "migrate", "--yes"] },
740
756
  affectedFiles: [
741
757
  ".kb/manifest.json",
742
758
  ".kb/migrations",
743
759
  ".kb/config.json",
760
+ ...(legacyStorage?.moves.map((move) => move.from) ?? []),
744
761
  ],
745
- evidence: { configStatus },
762
+ evidence: {
763
+ configStatus,
764
+ ...(legacyStorage === null
765
+ ? {}
766
+ : {
767
+ legacyStorage: {
768
+ moves: legacyStorage.moves,
769
+ blockers: legacyStorage.blockers,
770
+ legacyConfig: legacyStorage.legacyConfig,
771
+ },
772
+ }),
773
+ },
746
774
  dependsOn: storeState === "missing" ? ["branch-store-ensure"] : [],
747
- autoApplicable: configStatus.status !== "newer",
775
+ autoApplicable: !operatorOnly,
748
776
  }),
749
777
  );
750
778
  }
@@ -769,12 +797,16 @@ export function buildActionsFromStatus(input: {
769
797
  typeof store?.path === "string" ? store.path : ".kb/branches",
770
798
  ],
771
799
  evidence: { reason: uncompiled },
800
+ // A blocked schema step waits on an operator; the compile still runs
801
+ // from the .kb/ sources Kibi already reads.
772
802
  dependsOn: actions
773
- .map((action) => action.id)
774
803
  .filter(
775
- (id) =>
776
- id === "branch-store-ensure" || id === "schema-config-upgrade",
777
- ),
804
+ (action) =>
805
+ action.state === "ready" &&
806
+ (action.id === "branch-store-ensure" ||
807
+ action.id === "schema-config-upgrade"),
808
+ )
809
+ .map((action) => action.id),
778
810
  postconditions: [{ branchStore: "compiled" }],
779
811
  autoApplicable: true,
780
812
  }),
@@ -217,7 +217,7 @@ export const modelSpec = {
217
217
  name: "kb_model",
218
218
  cliName: "model",
219
219
  description:
220
- "Turn requirement prose into checkable structure without writing the KB. mode:analyze returns the semantic advisor receipt: the clause ledger, what is grounded, what is ambiguous and suggested modeling (pass clauses for compound prose and optional typed kibi.logic.v1 interpretations). mode:requirement converts one claim (subjectKey, propertyKey, operator, value, or typed logic) into a strict-lane write set and applyPlan. mode:predicates ranks ontology predicate schemas and returns an applicable predicate-fact plan only for reviewed bindings, otherwise an ontology-gap observation. Apply returned plans with kb_upsert.",
220
+ "Turn requirement prose into checkable structure without writing the KB. mode:analyze returns the semantic advisor receipt: the clause ledger, what is grounded, what is ambiguous and suggested modeling (pass clauses for compound prose and optional typed kibi.logic.v1 interpretations). mode:requirement converts one claim (subjectKey, propertyKey, operator, value, or typed logic) into a strict-lane write set and applyPlan. mode:predicates ranks ontology predicate schemas and returns an applicable predicate-fact plan only for reviewed bindings, otherwise an ontology-gap observation. With requirementId, mode:predicates also returns a hash-bound plan (fact write plus requires_predicate link) for kb_apply_plan, whose contradictionAnalysis is the what-if over the current KB; other returned plans apply with kb_upsert, and kb_upsert dryRun with plannedFacts previews a requirement before its facts exist.",
221
221
  businessInputSchema: {
222
222
  type: "object",
223
223
  required: ["mode", "text"],
@@ -165,8 +165,9 @@ export const searchSpec = {
165
165
  limit: {
166
166
  type: "integer",
167
167
  minimum: 0,
168
- default: 20,
169
- description: "Optional max rows to return after ranking. Default: 20.",
168
+ default: 10,
169
+ description:
170
+ "Optional max rows to return after ranking. Default: 10; raise it or page with offset when the answer layer is not enough.",
170
171
  },
171
172
  offset: {
172
173
  type: "integer",
@@ -19,22 +19,13 @@ export const ENTITY_TYPES = [
19
19
  "fact",
20
20
  ] as const;
21
21
 
22
- // An observation or meta fact may quote a claim in claim_text alone: it is a
23
- // non-grounding review note, so it does not claim the clause's key.
22
+ // A claim_key names the clause in claim_text, so it never travels alone. The
23
+ // reverse is open: Kibi derives a missing claim_key from claim_text on a
24
+ // grounding fact, and an observation or meta fact quotes a claim without one.
25
+ // implements REQ-kibi-review-observation-claim-text-v2
24
26
  const CLAIM_PROVENANCE_CONDITIONAL = JSON.parse(`{
25
- "if": {
26
- "anyOf": [
27
- { "required": ["claim_key"] },
28
- {
29
- "required": ["claim_text"],
30
- "not": {
31
- "properties": { "fact_kind": { "enum": ["observation", "meta"] } },
32
- "required": ["fact_kind"]
33
- }
34
- }
35
- ]
36
- },
37
- "then": { "required": ["claim_key", "claim_text"] }
27
+ "if": { "required": ["claim_key"] },
28
+ "then": { "required": ["claim_text"] }
38
29
  }`) as Record<string, unknown>;
39
30
 
40
31
  export const ENTITY_PROPERTIES_SCHEMA = {
@@ -293,12 +284,12 @@ export const ENTITY_PROPERTIES_SCHEMA = {
293
284
  type: "string",
294
285
  pattern: "^CLAIM-[A-F0-9]{16}$",
295
286
  description:
296
- "Stable semantic-advisor key for the atomic prose clause grounded by this fact.",
287
+ "Stable key of the atomic prose clause grounded by this fact, derived from claim_text. Optional: when omitted, Kibi derives it; when sent, it must equal the derived key.",
297
288
  },
298
289
  claim_text: {
299
290
  type: "string",
300
291
  description:
301
- "Exact atomic requirement clause represented by this fact, retained for human audit. Requires claim_key, except on an observation or meta fact, which may quote the clause it is about without claim_key.",
292
+ "Exact atomic requirement clause represented by this fact, retained for human audit. On a grounding fact Kibi derives claim_key from it; an observation or meta fact quotes the clause it is about without a key.",
302
293
  },
303
294
  predicate_name: {
304
295
  type: "string",
@@ -38,6 +38,21 @@ export const upsertSpec = {
38
38
  description:
39
39
  "Validate the payload and return schema/modeling errors, a normalized preview and the semantic advisor receipt without writing the KB or workspace. Use it as the preflight before the real write.",
40
40
  },
41
+ plannedFacts: {
42
+ type: "array",
43
+ description:
44
+ "dryRun only. Fact upsert payloads (type, id, properties, optional relationships) that will be written before this payload, typically the applyPlan entries kb_model returned. They are validated in order and staged with the payload, so a requires_predicate or requires_property target that does not exist yet resolves, its claim_key is read from the plan, and the contradiction preview runs against the KB as it would be after the whole batch (result: stagedFacts, whatIf). A conflict the batch would introduce, such as an allow and a deny permission_rule on the same key, is refused as the commit would refuse it. Rejected without dryRun: write the facts first.",
45
+ items: {
46
+ type: "object",
47
+ required: ["type", "id", "properties"],
48
+ properties: {
49
+ type: { type: "string", const: "fact" },
50
+ id: { type: "string", minLength: 1 },
51
+ properties: { type: "object" },
52
+ relationships: RELATIONSHIPS_SCHEMA,
53
+ },
54
+ },
55
+ },
41
56
  document: {
42
57
  type: "object",
43
58
  additionalProperties: false,
@@ -42,19 +42,8 @@ type VerificationPerspective = "internal" | "consumer";
42
42
 
43
43
  const factConditionals = JSON.parse(`[
44
44
  {
45
- "if": {
46
- "anyOf": [
47
- { "required": ["claim_key"] },
48
- {
49
- "required": ["claim_text"],
50
- "not": {
51
- "properties": { "fact_kind": { "enum": ["observation", "meta"] } },
52
- "required": ["fact_kind"]
53
- }
54
- }
55
- ]
56
- },
57
- "then": { "required": ["claim_key", "claim_text"] }
45
+ "if": { "required": ["claim_key"] },
46
+ "then": { "required": ["claim_text"] }
58
47
  },
59
48
  {
60
49
  "if": {
@@ -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.15.0
5
+ version: 2.15.3
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -82,6 +82,41 @@ different workspace than the one you asked for; its `detail.reason` says why.
82
82
  Use the CLI from your working directory instead: `npx --no-install kibi ...`
83
83
  runs where you are and needs no `workspaceRoot`.
84
84
 
85
+ ## Reading a hook snippet
86
+
87
+ On hosts where Kibi hooks are installed, a short block
88
+ arrives before you read or edit a file whose symbols are linked to
89
+ requirements. It is projected from `.kb/symbols.yaml`, not from the store, so
90
+ it is a pointer to the knowledge, never proof. Each line answers one question:
91
+
92
+ - `Kibi knowledge for <path> (symbol manifest):` and the `- REQ-… — <symbol
93
+ titles>` rows: the requirements the file's symbols implement, lead
94
+ requirement first. Read the lead requirement with `kb_query({id})` before
95
+ changing behavior.
96
+ - `REQ-… must keep true: FACT-…` (edit only): the ground facts the lead
97
+ requirement requires. An edit that breaks one of them is a requirement
98
+ change: supersede and remodel the requirement, do not code around the fact.
99
+ - `Decision: ADR-…` (edit only): the decision behind the lead requirement.
100
+ - `Covered by: TEST-…` and `Test code for: …`: the tests that prove the
101
+ file's symbols; run or update them with the edit.
102
+ - `The edit is inside <symbol>, which implements REQ-…`: the symbol the edit
103
+ lands in.
104
+ - `Next layer: …`: the exact `kb_query` and `kb_search(... sourceLocations)`
105
+ calls that return the full detail.
106
+ - The `kb_check({sourceFiles:[…], includeImpactDiagnostics:true,
107
+ includeWorkingTreeDiff:true})` line (edit only): run it after the edit. The
108
+ Stop hook reminds you once per session of files edited without it.
109
+
110
+ A file already shown this session gets only the one-line `Kibi: this edit to
111
+ <path> is inside <symbol>` update; the facts and tests from its first snippet
112
+ still apply. `Kibi: no symbol in <path> is linked to a requirement yet` means
113
+ the file is unowned: find the governing requirement with `kb_search` and
114
+ `sourceLocations`, then link a symbol to it with `implements` through
115
+ `kb_upsert`. Without a readable manifest (`Kibi: the hook could not read
116
+ .kb/symbols.yaml`) snippets are off for this session; `kb_query({sourceFile})`
117
+ still answers and `kibi sync` regenerates the manifest. The search tip and the
118
+ `.kb/` note appear once per session and need no action.
119
+
85
120
  ## Safe workflow
86
121
 
87
122
  1. Always discover before you mutate: start with `kb_search`, then exact-filter with `kb_query`. Use
@@ -334,6 +369,8 @@ For each relational clause, call `kb_model` with `mode: "predicates"`, that clau
334
369
  reviewed `schemaId`. If `binding_status` is `incomplete`, supply
335
370
  `argumentBindings` and retry. Persist `fact_kind: predicate` with the same
336
371
  `claim_key` / `claim_text`, then link `REQ -> fact` with `requires_predicate`.
372
+ `claim_key` is derived from `claim_text`, so a ground fact may send the exact
373
+ clause alone and Kibi fills the key in; a sent key must equal the derived one.
337
374
  Use `kb_model` with `mode: "requirement"` for strict scalar clauses. Use
338
375
  `fact_kind: observation` only for a true ontology gap. The manifest is not
339
376
  grounding: run `logic-coverage` so each key binds to one ground fact.
@@ -6,7 +6,7 @@ Requirement prose remains the human-readable source. Facts add queryable semanti
6
6
 
7
7
  Logical coverage is clause-based, not entity-based. Give `kb_model` (`mode: "analyze"`) the complete requirement body and verify that its `clauses` list contains every atomic obligation. Supply the `clauses` input yourself when automatic splitting would combine or omit obligations. For every normative clause:
8
8
 
9
- - preserve the returned stable key as `claim_key` and the exact clause as `claim_text` on its ground fact;
9
+ - put the exact clause as `claim_text` on its ground fact, with the returned stable key as `claim_key` or none (Kibi derives a missing key; a sent key must match);
10
10
  - add the key to the exact current `logic_claims` set, removing only stale keys whose propositions no longer exist in the source;
11
11
  - link it through exactly one `requires_property` for `property_value`, one `requires_predicate` for `predicate`, or one `requires_rule` for a safe `rule` fact;
12
12
  - leave ambiguity and ontology gaps visibly unresolved—observations do not satisfy `logic-coverage`.
@@ -68,14 +68,16 @@ id: FACT-SUSPENDED-PUBLISH-DENIED
68
68
  status: active
69
69
  fact_kind: predicate
70
70
  predicate_name: permission_rule
71
- predicate_args: [suspended_user, publish, articles, deny]
71
+ predicate_args: [suspended_user, publish, article, deny]
72
72
  polarity: deny
73
- canonical_key: permission_rule(suspended_user,publish,articles,deny)
73
+ canonical_key: permission_rule(suspended_user,publish,article,deny)
74
74
  claim_key: <advisor-issued key for this exact clause>
75
75
  claim_text: Suspended users must not publish articles.
76
76
  relationship: { type: requires_predicate, from: REQ-SUSPENDED-PUBLISH-DENIED, to: FACT-SUSPENDED-PUBLISH-DENIED }
77
77
  ```
78
78
 
79
+ Inferred bindings drop the article and singularise the resource (`articles` becomes `article`, `any analysis request` and `analysis requests` both become `analysis_request`), so an allow and a deny on the same actor, action and resource share one key. Grants read the same as `allowed`: `may`, `can`, `is allowed to`, `are permitted to`, `must be able to`, `are able to`, `have permission to` and `are entitled to` all select `permission_rule` with `polarity: assert`.
80
+
79
81
  If another current requirement links an `assert` fact with the same predicate namespace, name, and ordered arguments, `domain-contradictions` reports the pair. More complex semantic conflicts require a shared canonical schema and arguments; do not assume Kibi can prove arbitrary equivalence between differently shaped predicates.
80
82
 
81
83
  The full logical representation of a requirement is the conjunction of all of its linked ground property and predicate terms. Conditions, exceptions, permissions, and ordering constraints remain explicit arguments of their declared schemas. Contradiction detection therefore depends on equivalent prose reusing the same predicate name, argument roles, ordered canonical values, and opposing polarity rather than inventing synonymous terms.
@@ -126,8 +126,8 @@ diagnostics (`symbol_semantic_review_needed`).
126
126
  `requires_predicate`.
127
127
  3. Scalar placement/alignment/order clauses: `kb_model` (`mode: "requirement"`), apply the strict
128
128
  subject/property plan and `constrains` / `requires_property`.
129
- 4. Preserve `claim_key` / `claim_text`; merge every key into the requirement
130
- `logic_claims` manifest.
129
+ 4. Preserve `claim_key` / `claim_text` (a missing key is derived from the
130
+ text); merge every key into the requirement `logic_claims` manifest.
131
131
  5. `kb_upsert` with `dryRun: true`, create endpoints first, then sequential `kb_upsert`.
132
132
  6. `kb_check` with `logic-coverage`, `predicate-verifiability`, `domain-contradictions`,
133
133
  then a final unfiltered `kb_check`.
@@ -203,7 +203,7 @@ Optional per-project workflow for recording what the screen should look like. No
203
203
  2. Run `kb_model` with `mode: "analyze"` on the description; audit or supply `clauses`.
204
204
  3. For relational layout clauses ("X must remain visually aligned with Y"), call `kb_model` with `mode: "predicates"`, apply the returned `fact_kind: predicate` plan with `predicate_name: visual_layout_rule` and `requires_predicate`.
205
205
  4. For scalar placement, alignment, and ordering clauses, call `kb_model` with `mode: "requirement"` and apply strict `fact_kind: subject` / `fact_kind: property_value` facts linked with `constrains` / `requires_property`. Model header order as indexed property keys (`nav_order_1`, `nav_order_2`, ...) against one subject region.
206
- 5. Preserve `claim_key` / `claim_text` on every ground fact and merge every key into the requirement `logic_claims` manifest.
206
+ 5. Preserve `claim_key` / `claim_text` on every ground fact (a missing `claim_key` is derived from `claim_text`) and merge every key into the requirement `logic_claims` manifest.
207
207
  6. Model UI components as `symbol` entities with `sourceFile` and `symbol_role: behavioral`, linked `implements` to the requirement.
208
208
  7. `kb_upsert` with `dryRun: true`, create endpoints first, then sequential `kb_upsert`. Run `kb_check` with `logic-coverage`, `predicate-verifiability`, and `domain-contradictions`, then a final unfiltered `kb_check`.
209
209
 
@@ -1675,35 +1675,12 @@
1675
1675
  },
1676
1676
  {
1677
1677
  "if": {
1678
- "anyOf": [
1679
- {
1680
- "required": [
1681
- "claim_key"
1682
- ]
1683
- },
1684
- {
1685
- "required": [
1686
- "claim_text"
1687
- ],
1688
- "not": {
1689
- "properties": {
1690
- "fact_kind": {
1691
- "enum": [
1692
- "observation",
1693
- "meta"
1694
- ]
1695
- }
1696
- },
1697
- "required": [
1698
- "fact_kind"
1699
- ]
1700
- }
1701
- }
1678
+ "required": [
1679
+ "claim_key"
1702
1680
  ]
1703
1681
  },
1704
1682
  "then": {
1705
1683
  "required": [
1706
- "claim_key",
1707
1684
  "claim_text"
1708
1685
  ]
1709
1686
  }