kibi-cli 2.15.0 → 2.17.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 (81) hide show
  1. package/dist/cli-protocol.d.ts.map +1 -1
  2. package/dist/cli-protocol.js +13 -0
  3. package/dist/cli-register-proof.d.ts +6 -0
  4. package/dist/cli-register-proof.d.ts.map +1 -1
  5. package/dist/cli-register-proof.js +52 -2
  6. package/dist/commands/check.d.ts.map +1 -1
  7. package/dist/commands/check.js +5 -1
  8. package/dist/commands/doctor.d.ts.map +1 -1
  9. package/dist/commands/doctor.js +83 -4
  10. package/dist/commands/prove.js +1 -1
  11. package/dist/operations/modeling/model-requirement.d.ts +5 -0
  12. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  13. package/dist/operations/modeling/model-requirement.js +17 -5
  14. package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
  15. package/dist/operations/modeling/predicate-applicability.js +7 -1
  16. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  17. package/dist/operations/modeling/predicate-applyplan.js +16 -6
  18. package/dist/operations/modeling/predicate-utils.d.ts.map +1 -1
  19. package/dist/operations/modeling/predicate-utils.js +2 -3
  20. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  21. package/dist/operations/planning/apply-plan.js +38 -7
  22. package/dist/operations/planning/migration-refusal.d.ts +14 -0
  23. package/dist/operations/planning/migration-refusal.d.ts.map +1 -0
  24. package/dist/operations/planning/migration-refusal.js +17 -0
  25. package/dist/operations/semantic-advisor/shared.d.ts +7 -0
  26. package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
  27. package/dist/operations/semantic-advisor/shared.js +22 -3
  28. package/dist/proof/inspect.d.ts +20 -1
  29. package/dist/proof/inspect.d.ts.map +1 -1
  30. package/dist/proof/inspect.js +17 -8
  31. package/dist/proof/integration-plan.d.ts +58 -0
  32. package/dist/proof/integration-plan.d.ts.map +1 -0
  33. package/dist/proof/integration-plan.js +348 -0
  34. package/dist/proof/integrations.js +1 -1
  35. package/dist/public/impact/coverage-depth-quality.d.ts.map +1 -1
  36. package/dist/public/impact/coverage-depth-quality.js +29 -1
  37. package/dist/public/operations/catalog.d.ts +2 -2
  38. package/dist/public/operations/check-executor.d.ts +16 -0
  39. package/dist/public/operations/check-executor.d.ts.map +1 -1
  40. package/dist/public/operations/check-executor.js +80 -0
  41. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  42. package/dist/public/operations/discovery-executors.js +13 -3
  43. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  44. package/dist/public/operations/migration-plan.js +28 -0
  45. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  46. package/dist/public/operations/result-envelope.js +23 -3
  47. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  48. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  49. package/dist/public/operations/specs/check-rules.generated.js +2 -1
  50. package/dist/public/operations/specs/check.d.ts +1 -1
  51. package/dist/public/operations/specs/composite.d.ts +16 -3
  52. package/dist/public/operations/specs/composite.d.ts.map +1 -1
  53. package/dist/public/operations/specs/composite.js +79 -2
  54. package/dist/public/operations/specs/modeling.d.ts +1 -1
  55. package/dist/public/operations/specs/modeling.js +1 -1
  56. package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -1
  57. package/dist/public/skills/kibi-usage/SKILL.md +21 -7
  58. package/dist/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
  59. package/dist/public/skills/kibi-usage/resources/proof.md +34 -4
  60. package/dist/utils/branch-store.d.ts +32 -0
  61. package/dist/utils/branch-store.d.ts.map +1 -1
  62. package/dist/utils/branch-store.js +122 -1
  63. package/dist/utils/rule-registry.generated.d.ts +5 -0
  64. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  65. package/dist/utils/rule-registry.generated.js +6 -0
  66. package/dist/utils/strict-modeling.d.ts +12 -0
  67. package/dist/utils/strict-modeling.d.ts.map +1 -1
  68. package/dist/utils/strict-modeling.js +12 -1
  69. package/package.json +2 -2
  70. package/src/public/impact/coverage-depth-quality.ts +46 -1
  71. package/src/public/operations/check-executor.ts +105 -0
  72. package/src/public/operations/discovery-executors.ts +19 -2
  73. package/src/public/operations/migration-plan.ts +35 -0
  74. package/src/public/operations/result-envelope.ts +31 -3
  75. package/src/public/operations/specs/check-rules.generated.ts +2 -1
  76. package/src/public/operations/specs/composite.ts +110 -11
  77. package/src/public/operations/specs/modeling.ts +1 -1
  78. package/src/public/skills/kibi-bootstrap/SKILL.md +2 -1
  79. package/src/public/skills/kibi-usage/SKILL.md +21 -7
  80. package/src/public/skills/kibi-usage/resources/fact-lanes.md +3 -3
  81. package/src/public/skills/kibi-usage/resources/proof.md +34 -4
@@ -16,10 +16,12 @@ import { rankEntities } from "../../search-ranking.js";
16
16
  import type { SearchMatch } from "../../search-ranking.js";
17
17
  import { resolveBranchAttachment } from "../../utils/branch-resolver.js";
18
18
  import {
19
+ BRANCH_STORE_NOT_COMPILED,
19
20
  type BranchStoreInspection,
20
21
  branchStoreReason,
21
22
  inspectBranchStore,
22
23
  storeLockJournalReason,
24
+ uncompiledBranchStoreReason,
23
25
  } from "../../utils/branch-store.js";
24
26
  import {
25
27
  loadEntities,
@@ -459,7 +461,17 @@ export async function executeStatus(
459
461
  }
460
462
  const snapshotEvidence = await readWorkspaceSnapshot(context);
461
463
  const existingReasons = payload.staleReasons ?? [];
462
- const storeReason = branchStoreReason(store);
464
+ // implements REQ-cli-status-pre-first-sync
465
+ // A missing or empty store beside authored sources gets the one reason
466
+ // whose remediation works (kibi sync); `kibi branch ensure` alone would
467
+ // leave the store empty.
468
+ const storeReason =
469
+ uncompiledBranchStoreReason(
470
+ context.workspaceRoot,
471
+ store,
472
+ attachment.kbBranch,
473
+ ) ?? branchStoreReason(store);
474
+ const storeUncompiled = storeReason?.code === BRANCH_STORE_NOT_COMPILED;
463
475
  const lockReason = storeLockJournalReason(store.path);
464
476
  const compilerReason = compilerFingerprintReason(store.path);
465
477
  const engineReason = engineStatus
@@ -495,6 +507,11 @@ export async function executeStatus(
495
507
  // Source hashes can all match while the compilation itself is lossy, so
496
508
  // a compiler contract change alone makes the store stale.
497
509
  ...(compilerReason ? { syncState: "stale", dirty: true } : {}),
510
+ // A missing or empty store has no stale file to report, yet nothing in
511
+ // it is current: the authored sources were never compiled. (A missing
512
+ // store without authored sources, and an unreadable or incomplete one,
513
+ // stay "unknown".)
514
+ ...(storeUncompiled ? { syncState: "stale", dirty: true } : {}),
498
515
  branchAttachment: attachment,
499
516
  branchStore: store,
500
517
  ...(engineStatus ? { engineStatus } : {}),
@@ -644,7 +661,7 @@ export async function executeStatus(
644
661
  content: [
645
662
  {
646
663
  type: "text",
647
- text: `Branch ${payload.branch} is ${payload.syncState} (snapshot ${payload.snapshotId}, dirty=${payload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})`,
664
+ text: `Branch ${payload.branch} is ${enrichedPayload.syncState} (snapshot ${payload.snapshotId}, dirty=${enrichedPayload.dirty}, proofSnapshot=${enrichedPayload.proofSnapshot})${storeUncompiled ? `. Blocking: ${String(storeReason?.detail)} Run kibi sync.` : ""}`,
648
665
  },
649
666
  ],
650
667
  structuredContent: statusWithPlan,
@@ -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 { BRANCH_STORE_NOT_COMPILED } from "../../utils/branch-store.js";
4
5
  import { legacyConfigExists } from "../../utils/config.js";
5
6
  import { readKbManifestStatus } from "../../utils/kb-manifest.js";
6
7
  import {
@@ -744,8 +745,42 @@ export function buildActionsFromStatus(input: {
744
745
  );
745
746
  }
746
747
 
748
+ // implements REQ-cli-status-pre-first-sync
749
+ // A branch store that was never compiled while .kb/ holds authored sources
750
+ // is fixed only by compiling it: `kibi branch ensure` alone leaves an empty
751
+ // manifest. The compile step runs after the store exists and after any
752
+ // schema migration, so it compiles the migrated sources.
753
+ const uncompiled = (input.staleReasons ?? []).find(
754
+ (reason) => reason.code === BRANCH_STORE_NOT_COMPILED,
755
+ );
756
+ if (uncompiled !== undefined) {
757
+ actions.push(
758
+ migrationAction({
759
+ id: "branch-store-compile",
760
+ code: BRANCH_STORE_NOT_COMPILED,
761
+ category: "storage",
762
+ safety: "automatic",
763
+ invocation: { kind: "cli", command_argv: ["kibi", "sync"] },
764
+ affectedFiles: [
765
+ typeof store?.path === "string" ? store.path : ".kb/branches",
766
+ ],
767
+ evidence: { reason: uncompiled },
768
+ dependsOn: actions
769
+ .map((action) => action.id)
770
+ .filter(
771
+ (id) =>
772
+ id === "branch-store-ensure" || id === "schema-config-upgrade",
773
+ ),
774
+ postconditions: [{ branchStore: "compiled" }],
775
+ autoApplicable: true,
776
+ }),
777
+ );
778
+ }
779
+
747
780
  for (const reason of input.staleReasons ?? []) {
748
781
  const code = typeof reason.code === "string" ? reason.code : "stale_source";
782
+ // Planned above as an automatic compile step, not a review.
783
+ if (code === BRANCH_STORE_NOT_COMPILED) continue;
749
784
  const file = typeof reason.path === "string" ? reason.path : "";
750
785
  actions.push(
751
786
  migrationAction({
@@ -1,3 +1,4 @@
1
+ import { MIGRATION_PLAN_REFUSED } from "../../operations/planning/migration-refusal.js";
1
2
  import type { KibiResult, OperationEffect, OperationName } from "./types.js";
2
3
 
3
4
  export const KIBI_PROTOCOL_VERSION = 1 as const;
@@ -139,6 +140,16 @@ export function resultVersion(spec: {
139
140
  return spec.resultVersion ?? `kibi.${spec.name}.v1`;
140
141
  }
141
142
 
143
+ function refusalDetail(row: Record<string, unknown>): string {
144
+ const results = Array.isArray(row.actionResults) ? row.actionResults : [];
145
+ const failure = results.find(
146
+ (entry) => record(entry) && entry.outcome === "failed",
147
+ );
148
+ return record(failure) && typeof failure.detail === "string"
149
+ ? failure.detail
150
+ : "";
151
+ }
152
+
142
153
  export function toKibiResult<T>(
143
154
  spec: {
144
155
  readonly name: OperationName | string;
@@ -158,12 +169,17 @@ export function toKibiResult<T>(
158
169
  } = {},
159
170
  ): KibiResult<T> {
160
171
  const row = record(data) ? data : undefined;
172
+ // A migration plan whose actions were all refused changed nothing: it is
173
+ // an error, not a success that needs reconciliation.
174
+ const migrationRefused =
175
+ row?.version === "kibi.migration-apply-result.v1" &&
176
+ row.outcome === "refused";
161
177
  let status = options.status;
162
178
  if (!status) {
163
179
  status = "success";
164
180
  if (row?.status === "committed_with_repairs")
165
181
  status = "committed_with_repairs";
166
- if (row?.status === "rejected") status = "error";
182
+ if (row?.status === "rejected" || migrationRefused) status = "error";
167
183
  }
168
184
  const terminalError =
169
185
  row?.status === "rejected"
@@ -173,9 +189,21 @@ export function toKibiResult<T>(
173
189
  "Bootstrap stopped at a deterministic failure. Inspect actionResults for committed actions and re-plan from the current state.",
174
190
  retryable: false,
175
191
  }
176
- : undefined;
192
+ : migrationRefused
193
+ ? {
194
+ code: MIGRATION_PLAN_REFUSED,
195
+ message:
196
+ `Migration plan refused; nothing was changed. ${refusalDetail(row)}`.trim(),
197
+ retryable: false,
198
+ }
199
+ : undefined;
177
200
  const resultError = options.error ?? terminalError;
178
- const outcome = effectOutcome(status, options);
201
+ const outcome = effectOutcome(
202
+ status,
203
+ migrationRefused && options.attempted === undefined
204
+ ? { ...options, attempted: false }
205
+ : options,
206
+ );
179
207
  return {
180
208
  kibiProtocol: KIBI_PROTOCOL_VERSION,
181
209
  operation: spec.name as OperationName,
@@ -15,6 +15,7 @@ export const SELECTABLE_RULE_NAMES: readonly string[] = [
15
15
  "symbol-traceability",
16
16
  "no-dangling-refs",
17
17
  "source-relationship-parity",
18
+ "branch-store-not-compiled",
18
19
  "source-path-dangling",
19
20
  "no-cycles",
20
21
  "required-fields",
@@ -61,4 +62,4 @@ export const SELECTABLE_RULE_NAMES: readonly string[] = [
61
62
  // implements REQ-006
62
63
  /** Comma-separated "Allowed:" fragment for the kb_check rules description. */
63
64
  export const SELECTABLE_RULES_ALLOWED =
64
- "must-priority-coverage, symbol-coverage, symbol-traceability, no-dangling-refs, source-relationship-parity, source-path-dangling, no-cycles, required-fields, deprecated-adr-no-successor, superseded-requirement-open, scenario-feasibility, scenario-feasibility-unknown, exception-claim-keys, numeric-string-value, rule-key-arguments-missing, domain-contradictions, logic-coverage, rule-safety, rule-verifiability, query-plan-safety, req-status-vocabulary, strict-fact-shape, strict-req-fact-pairing, predicate-verifiability, strict-readiness, semantic-completeness, related-requirement-unmodeled, proof-contract-symbols, entity-id-style, domain-redundancy, domain-implication, subject-key-identity, subject-key-shape, ontology-quality, exception-unapproved, exception-approval-self-attested, agent-requirement-unapproved, requirement-rationale-missing, symbol-owner-superseded, adr-unlinked, adr-proposed, predicate-schema-conformance, entity-context-missing, entity-context-acknowledged, policy-ownership, policy-markers";
65
+ "must-priority-coverage, symbol-coverage, symbol-traceability, no-dangling-refs, source-relationship-parity, branch-store-not-compiled, source-path-dangling, no-cycles, required-fields, deprecated-adr-no-successor, superseded-requirement-open, scenario-feasibility, scenario-feasibility-unknown, exception-claim-keys, numeric-string-value, rule-key-arguments-missing, domain-contradictions, logic-coverage, rule-safety, rule-verifiability, query-plan-safety, req-status-vocabulary, strict-fact-shape, strict-req-fact-pairing, predicate-verifiability, strict-readiness, semantic-completeness, related-requirement-unmodeled, proof-contract-symbols, entity-id-style, domain-redundancy, domain-implication, subject-key-identity, subject-key-shape, ontology-quality, exception-unapproved, exception-approval-self-attested, agent-requirement-unapproved, requirement-rationale-missing, symbol-owner-superseded, adr-unlinked, adr-proposed, predicate-schema-conformance, entity-context-missing, entity-context-acknowledged, policy-ownership, policy-markers";
@@ -29,17 +29,76 @@ function requiredFields(spec: {
29
29
  return Array.isArray(required) ? (required as string[]) : [];
30
30
  }
31
31
 
32
+ type CompositeRoute = {
33
+ readonly name?: string;
34
+ readonly execute: Executor;
35
+ readonly businessInputSchema: Readonly<Record<string, unknown>>;
36
+ };
37
+
38
+ /**
39
+ * Per route, the parameters another route of the same composite owns that
40
+ * mean the same thing here under another name. kb_model mode requirement
41
+ * takes the claim's subject as subjectKey; mode predicates takes it as
42
+ * subjectHint.
43
+ */
44
+ const EQUIVALENT_PARAMETERS: Readonly<
45
+ Record<string, Readonly<Record<string, string>>>
46
+ > = {
47
+ kb_suggest_predicates: { subjectKey: "subjectHint" },
48
+ kb_model_requirement: { subjectHint: "subjectKey" },
49
+ };
50
+
51
+ /** Routed operations whose `warnings` are `{ kind, message, nextAction }`. */
52
+ const RECORD_WARNING_OPERATIONS = new Set(["kb_model_requirement"]);
53
+
54
+ // implements REQ-kibi-mcp-tool-consolidation
55
+ /**
56
+ * A composite accepts every route's parameters, so a parameter of another
57
+ * route passes schema validation and the chosen route silently ignores it.
58
+ * Name each one instead, with the parameter this route uses for the same
59
+ * thing when there is one.
60
+ */
61
+ export function foreignParameterWarnings(
62
+ selector: "action" | "mode",
63
+ choice: string,
64
+ routes: Readonly<Record<string, CompositeRoute>>,
65
+ rest: Readonly<Record<string, unknown>>,
66
+ ): Array<{ parameter: string; message: string; nextAction: string }> {
67
+ const route = routes[choice];
68
+ if (route === undefined) return [];
69
+ const own = new Set(Object.keys(properties(route)));
70
+ const equivalents =
71
+ route.name === undefined ? {} : (EQUIVALENT_PARAMETERS[route.name] ?? {});
72
+ const warnings: Array<{
73
+ parameter: string;
74
+ message: string;
75
+ nextAction: string;
76
+ }> = [];
77
+ for (const parameter of Object.keys(rest)) {
78
+ if (own.has(parameter) || rest[parameter] === undefined) continue;
79
+ const owners = Object.entries(routes)
80
+ .filter(
81
+ ([other, otherRoute]) =>
82
+ other !== choice && parameter in properties(otherRoute),
83
+ )
84
+ .map(([other]) => `${selector}: ${other}`);
85
+ if (owners.length === 0) continue;
86
+ const equivalent = equivalents[parameter];
87
+ warnings.push({
88
+ parameter,
89
+ message: `${parameter} is a ${owners.join(" / ")} argument; ${selector}: ${choice} ignored it.`,
90
+ nextAction:
91
+ equivalent !== undefined
92
+ ? `Use ${equivalent} in ${selector}: ${choice}.`
93
+ : `Drop ${parameter}, or switch to ${owners.join(" / ")} if that is what you meant.`,
94
+ });
95
+ }
96
+ return warnings;
97
+ }
98
+
32
99
  export async function dispatchComposite(
33
100
  selector: "action" | "mode",
34
- routes: Readonly<
35
- Record<
36
- string,
37
- {
38
- readonly execute: Executor;
39
- readonly businessInputSchema: Readonly<Record<string, unknown>>;
40
- }
41
- >
42
- >,
101
+ routes: Readonly<Record<string, CompositeRoute>>,
43
102
  input: Readonly<Record<string, unknown>>,
44
103
  context: OperationContext,
45
104
  ): Promise<OperationResult<Record<string, unknown>>> {
@@ -69,9 +128,49 @@ export async function dispatchComposite(
69
128
  wrapped && Array.isArray(result.content)
70
129
  ? (result.content as OperationResult["content"])
71
130
  : [{ type: "text", text: `${selector} ${String(choice)} completed` }];
131
+ const ignored = foreignParameterWarnings(
132
+ selector,
133
+ String(choice),
134
+ routes,
135
+ rest,
136
+ );
137
+ if (ignored.length === 0) {
138
+ return {
139
+ content,
140
+ structuredContent: { [selector]: choice, ...payload },
141
+ };
142
+ }
143
+ // The routed payload's own warnings list carries them, in its own shape;
144
+ // a payload without one gets them in the text content only.
145
+ const asRecords =
146
+ route.name !== undefined && RECORD_WARNING_OPERATIONS.has(route.name);
147
+ const routedWarnings = Array.isArray(payload.warnings)
148
+ ? {
149
+ warnings: [
150
+ ...payload.warnings,
151
+ ...ignored.map((warning) =>
152
+ asRecords
153
+ ? {
154
+ kind: "parameter_ignored",
155
+ message: warning.message,
156
+ nextAction: warning.nextAction,
157
+ }
158
+ : `${warning.message} ${warning.nextAction}`,
159
+ ),
160
+ ],
161
+ }
162
+ : {};
72
163
  return {
73
- content,
74
- structuredContent: { [selector]: choice, ...payload },
164
+ content: [
165
+ ...content,
166
+ {
167
+ type: "text",
168
+ text: ignored
169
+ .map((warning) => `${warning.message} ${warning.nextAction}`)
170
+ .join("\n"),
171
+ },
172
+ ],
173
+ structuredContent: { [selector]: choice, ...payload, ...routedWarnings },
75
174
  };
76
175
  }
77
176
 
@@ -78,7 +78,7 @@ export const modelRequirementSpec = {
78
78
  requirementId: {
79
79
  type: "string",
80
80
  description:
81
- "Optional existing requirement ID to receive the requires_rule relationship.",
81
+ "Optional requirement ID. The strict write set updates this requirement instead of minting a REQ-AUTO id, and a typed logic rule attaches its requires_rule relationship to it.",
82
82
  },
83
83
  claimKey: {
84
84
  type: "string",
@@ -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.10.0
5
+ version: 3.11.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -46,6 +46,7 @@ Seed branch-local Kibi knowledge from cited product intent, without creating a p
46
46
  - **Scenarios.** Write scenarios from the cited ticket's acceptance criteria, one scenario per criterion, not bulk drafts from the requirement title; ask the human when the source states none. Give each a plain title, the scenario prose in `document.body` (never in `properties`), and `assumes` links to the `property_value` facts whose values the scenario relies on. Set `expects` only when the scenario links `assumes` facts (or its criterion names concrete values that become them); `expects` without `assumes` only yields a feasibility-unknown warning, so a draft tagged `needs-human-review` omits `expects`. Then link the requirement with `specified_by` (upsert the `req` with its stored `title`, a `status` and that relationship only). Example: criterion "Given an annotation in draft state, when the user deletes it, the draft is cleared" becomes `{"type":"scenario","id":"SCEN-annotation-delete-clears-draft","properties":{"title":"Deleting a draft annotation clears the draft","status":"active","expects":"success"},"document":{"body":"Given an annotation in draft state, when the user deletes it, then the draft is cleared and nothing is published.\n\nThe acceptance criterion comes from the cited ticket; reviewers asked for it so a deleted draft never reappears after reload.\n"},"relationships":[{"type":"assumes","from":"SCEN-annotation-delete-clears-draft","to":"FACT-annotation-state-draft"}]}`, where the assumed fact is a `property_value` (`annotation.state` `eq` `draft`). Link existing tests that exercise it; never invent test evidence.
47
47
  - **Predicates.** Run `kb_model` with `mode: "predicates"` and `requirementId` on each persisted requirement and apply a returned plan with `requires_predicate` to its `relationshipTarget` (the planned `FACT-PRED-…` id, never a `SUGGEST-…` candidate id), or record the `review:ontology-gap` observation it returns, with its prose in `document.body`. A non-empty `existingGrounding` means the bootstrap already grounded that claim: on `replace_grounding` leave it unless the human wants the `replacementPlan` swap; on `provide_argument_bindings` retry with the listed arguments bound; on `record_ontology_gap` still write the returned gap observation, which is not a grounding link. Count what `kb_query` shows afterwards, not the calls you made.
48
48
  - **Unrecorded ambiguity.** Record any conflict or open question you found but did not declare in step 3 as a cited `fact_kind: observation` tagged `review:conflict` or `review:open-question`.
49
+ - **Proof integration.** Run `kibi proof inspect --json`. When it returns an `integrationPlan` (a `command` integration for the detected test runner), show the human the proposed command and `contractDefaults`, and on approval apply that exact plan with `kb_apply_plan` (`plan`, `approvedPlanHash` = its `planHash`, `approvedActionIds`); it writes `.kb/proof/integrations.json`, which you never hand-write. With no runner detected, report that proof stays deferred. Proof-bearing tests then name the integration in `proof_contract.integration`, and `kibi prove` records their evidence.
49
50
  Finish with `kb_check` and `kb_status`.
50
51
  12. **Report** calls made, result statuses, applied scope, what step 11 authored, gaps, and follow-ups. Classify as complete, partial, awaiting approval, or blocked.
51
52
 
@@ -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.11.0
5
+ version: 2.13.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -148,7 +148,13 @@ error `BOOTSTRAP_PLAN_REJECTED` can follow earlier committed actions: inspect
148
148
  `data.actionResults` and `data.applied`, then re-plan from the current state.
149
149
  Its journal is terminal; never recover or replay it. `BOOTSTRAP_PLAN_INVALID`
150
150
  fails preflight before bootstrap writes or a new journal; obtain a corrected
151
- plan. If the status is
151
+ plan. A migration plan whose actions were refused before changing anything
152
+ returns `status: error`, `MIGRATION_PLAN_REFUSED` and `data.outcome:
153
+ refused`: nothing changed, so read the action `detail` and get a new plan.
154
+ `data.outcome: reconciliation_required` (status success) means an action
155
+ failed mid-way: inspect its effect before retrying. `kb_delete` of an
156
+ authored entity returns status success with `deleted: 0`; its `errors` say
157
+ why nothing was deleted. If the status is
152
158
  `committed_with_repairs`, the mutation already committed: inspect failed effects,
153
159
  execute each required repair action in order, and never retry the original
154
160
  operation. Record effect failures, followed actions, and unsafe retries in
@@ -219,11 +225,19 @@ authored. Each one is the order the write path accepts.
219
225
  `semantic_inventory_version`, `semantic_source_field` and
220
226
  `semantic_source_hash`, the prose into `semantic_text`, `propositions`
221
227
  into `semantic_inventory` and the assertive `claim_key`s into
222
- `logic_claims`. `kb_model` mode `predicates` per clause; `kb_upsert` the
223
- returned `applyPlan` fact first (it carries the clause's `claim_key`).
224
- Then write the requirement in one `kb_upsert` with the ledger (that
225
- clause `status: modeled`), a sectioned `document.body`, `constrains` to
226
- the subject fact and `requires_predicate` to `relationshipTarget`. A
228
+ `logic_claims`. Reuse (or `kb_upsert`) the subject fact, then call
229
+ `kb_model` mode `predicates` per clause with `subjectHint` set to that
230
+ subject fact's `subject_key`: the requirement constrains nothing yet, so
231
+ the hint is the planned subject and the planned fact gets its
232
+ `subject_key` (`subjectKey` is a mode `requirement` parameter; mode
233
+ `predicates` ignores it and warns). `kb_upsert` the returned `applyPlan`
234
+ fact first (it carries the clause's `claim_key`). Then write the
235
+ requirement in one `kb_upsert` with the ledger (that clause `status:
236
+ modeled`), a sectioned `document.body`, `constrains` to the subject
237
+ fact, `requires_predicate` to `relationshipTarget` and `specified_by` to
238
+ its scenario: `specified_by` (req → scenario) is sent in the
239
+ requirement's write, never in the scenario's, so create the scenario
240
+ first. A
227
241
  `modeled` entry and its grounding link must arrive in the same write
228
242
  (`modeled semantic_inventory entries (n) must equal logical grounding
229
243
  relationships (m)`); a clause not grounded yet stays `status: missing`
@@ -61,16 +61,16 @@ A schema can close an argument with `argument_constants` (allowed values keyed b
61
61
 
62
62
  ### Deny predicate
63
63
 
64
- Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity:
64
+ Claim: “Suspended users must not publish articles.” When `permission_rule` fits, preserve its positive schema name and encode prohibition as polarity. `kb_model` (`mode: "predicates"`) plans this fact for the claim; the built-in schema's fourth argument, `decision`, repeats the polarity:
65
65
 
66
66
  ```yaml
67
67
  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, article]
71
+ predicate_args: [suspended_user, publish, articles, deny]
72
72
  polarity: deny
73
- canonical_key: permission_rule(suspended_user,publish,article)
73
+ canonical_key: permission_rule(suspended_user,publish,articles,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 }
@@ -51,14 +51,41 @@ proof_bindings: # optional provenance metadata
51
51
 
52
52
  Provenance lives in `proof_bindings`; the contract stays semantic.
53
53
 
54
+ - Every `required_proofs[].symbol_id` must name an existing `symbol` entity,
55
+ or `kb_check` reports `proof-contract-symbols`. Create the symbols first
56
+ (`kb_upsert`, narrow method-level granularity, never a `type-shape`
57
+ symbol): a production symbol `implements` the requirement and is
58
+ `covered_by` the test; an executable test symbol is `executable_for` it.
59
+ - `proof_bindings[].source_file`, when set, must equal the named symbol's
60
+ `sourceFile`: for a production symbol that is the production file, not the
61
+ spec file that exercises it. Omit it when unsure.
62
+
54
63
  ## Integration configuration
55
64
 
56
65
  `.kb/proof/integrations.json` (`kibi.proof-integration.v1`) is tracked,
57
- Kibi-managed configuration describing evidence production only:
66
+ Kibi-managed configuration describing evidence production only. Never
67
+ hand-write it: `kibi proof inspect --json` proposes a `command` integration
68
+ for the detected test runner (the `package.json` `test` script first) and
69
+ returns `integrationPlan`, a hash-bound `kibi.migration-plan.v2`, plus the
70
+ `contractDefaults` a proof-bearing test's `proof_contract` uses. Show the
71
+ proposal to the human, then apply the plan with `kb_apply_plan` (or
72
+ `kibi apply-plan --input -`) passing `plan`, `approvedPlanHash` (the plan's
73
+ `planHash`) and `approvedActionIds`. Once the file exists, a create plan is
74
+ refused (`status: error`, `MIGRATION_PLAN_REFUSED`, `data.outcome: refused`;
75
+ nothing is written); `kibi proof inspect --update <id> --json` plans adding or
76
+ replacing one named integration.
58
77
 
59
78
  - `producer: command` — Kibi synthesizes the envelope from the process
60
79
  outcome; obligations are bound with `aggregate_run` provenance and the
61
- single process invocation counts as the known first attempt.
80
+ single process invocation counts as the known first attempt. The run is
81
+ judged as a whole: any failing test in it fails every obligation of every
82
+ test that names the integration, so a suite with unrelated failures proves
83
+ nothing. Narrow the command to the proof-bearing tests with
84
+ `kibi proof inspect --update <id> --command "<command>" --json` (without
85
+ `--command` the proposal is the `package.json` `test` script or the
86
+ detected runner again), or make the run attributable per test: have the
87
+ command write a `kibi.proof-test-report.v1` to the path in
88
+ `KIBI_PROOF_TEST_REPORT` so each test is judged by its own steps.
62
89
  - `producer: playwright` (or custom) — the child emits `kibi.proof-run.v1`
63
90
  itself (bundled reporter: `kibi-cli/playwright-reporter`).
64
91
  - `producer: junit` / `tap` — Kibi converts the native report at `artifact`
@@ -82,8 +109,11 @@ effective fingerprint and do.
82
109
 
83
110
  ## Common rejections
84
111
 
85
- - `No proof integration configuration` — bootstrap has not configured proof
86
- for this repository yet; do not hand-wave, run the bootstrap proof step.
112
+ - `No proof integration configuration` — proof is not configured for this
113
+ repository yet; run `kibi proof inspect --json`, review its
114
+ `integrationPlan` and apply it with `kb_apply_plan`.
115
+ - `integration '<id>' ... is not configured` — the test names an integration
116
+ the file lacks; plan it with `kibi proof inspect --update <id> --json`.
87
117
  - `captured snapshot is not the live workspace snapshot` — the tree changed
88
118
  between capture and ingest; re-run `kibi prove`.
89
119
  - `artifact command_argv does not match the configured command` — the