kibi-cli 2.17.0 → 2.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/cli-operation-metadata.d.ts +1 -1
  2. package/dist/cli-operation-metadata.js +1 -1
  3. package/dist/cli-register-proof.js +9 -1
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +18 -6
  6. package/dist/commands/sync/manifest.d.ts +10 -0
  7. package/dist/commands/sync/manifest.d.ts.map +1 -1
  8. package/dist/commands/sync/manifest.js +27 -0
  9. package/dist/commands/sync/untracked-targets.d.ts +17 -0
  10. package/dist/commands/sync/untracked-targets.d.ts.map +1 -1
  11. package/dist/commands/sync/untracked-targets.js +81 -9
  12. package/dist/commands/sync.d.ts.map +1 -1
  13. package/dist/commands/sync.js +76 -6
  14. package/dist/engine.d.ts +4 -0
  15. package/dist/engine.d.ts.map +1 -1
  16. package/dist/engine.js +56 -33
  17. package/dist/operations/modeling/predicate-applicability.d.ts.map +1 -1
  18. package/dist/operations/modeling/predicate-applicability.js +5 -2
  19. package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
  20. package/dist/operations/modeling/predicate-catalog-4.js +4 -0
  21. package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
  22. package/dist/operations/modeling/predicate-loader.js +3 -0
  23. package/dist/operations/modeling/predicate-score-3.js +1 -1
  24. package/dist/operations/modeling/predicate-types.d.ts +5 -0
  25. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  26. package/dist/operations/mutation/source-authoring.d.ts +9 -0
  27. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  28. package/dist/operations/mutation/source-authoring.js +30 -1
  29. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  30. package/dist/operations/planning/apply-plan.js +10 -1
  31. package/dist/operations/semantic-advisor/clauses.d.ts.map +1 -1
  32. package/dist/operations/semantic-advisor/clauses.js +7 -1
  33. package/dist/prolog/store-lock.d.ts +41 -0
  34. package/dist/prolog/store-lock.d.ts.map +1 -1
  35. package/dist/prolog/store-lock.js +91 -1
  36. package/dist/prolog.d.ts +7 -1
  37. package/dist/prolog.d.ts.map +1 -1
  38. package/dist/prolog.js +21 -3
  39. package/dist/proof/inspect.d.ts +8 -1
  40. package/dist/proof/inspect.d.ts.map +1 -1
  41. package/dist/proof/inspect.js +19 -2
  42. package/dist/public/operations/catalog.d.ts +50 -1
  43. package/dist/public/operations/catalog.d.ts.map +1 -1
  44. package/dist/public/operations/contracts.d.ts.map +1 -1
  45. package/dist/public/operations/contracts.js +4 -4
  46. package/dist/public/operations/discovery-executors.d.ts +50 -1
  47. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  48. package/dist/public/operations/discovery-executors.js +418 -8
  49. package/dist/public/operations/migration-plan.d.ts +2 -0
  50. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  51. package/dist/public/operations/migration-plan.js +31 -0
  52. package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
  53. package/dist/public/operations/source-relationship-parity.js +55 -1
  54. package/dist/public/operations/specs/discovery.d.ts +50 -1
  55. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  56. package/dist/public/operations/specs/discovery.js +56 -1
  57. package/dist/public/skills/kibi-usage/SKILL.md +20 -3
  58. package/dist/public/skills/kibi-usage/resources/proof.md +23 -0
  59. package/dist/runtime/cli-runtime.d.ts.map +1 -1
  60. package/dist/runtime/cli-runtime.js +7 -2
  61. package/dist/search-answer.d.ts +12 -3
  62. package/dist/search-answer.d.ts.map +1 -1
  63. package/dist/search-answer.js +51 -4
  64. package/dist/utils/branch-store.d.ts +8 -0
  65. package/dist/utils/branch-store.d.ts.map +1 -1
  66. package/dist/utils/branch-store.js +27 -1
  67. package/dist/utils/content-freshness.d.ts +13 -0
  68. package/dist/utils/content-freshness.d.ts.map +1 -0
  69. package/dist/utils/content-freshness.js +171 -0
  70. package/dist/utils/symbol-manifest-layout.d.ts +9 -0
  71. package/dist/utils/symbol-manifest-layout.d.ts.map +1 -0
  72. package/dist/utils/symbol-manifest-layout.js +18 -0
  73. package/package.json +2 -2
  74. package/src/public/operations/contracts.ts +7 -4
  75. package/src/public/operations/discovery-executors.ts +556 -6
  76. package/src/public/operations/migration-plan.ts +44 -0
  77. package/src/public/operations/source-relationship-parity.ts +60 -3
  78. package/src/public/operations/specs/discovery.ts +67 -1
  79. package/src/public/skills/kibi-usage/SKILL.md +20 -3
  80. package/src/public/skills/kibi-usage/resources/proof.md +23 -0
@@ -3,6 +3,7 @@ import { existsSync, readFileSync } from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import { BRANCH_STORE_NOT_COMPILED } from "../../utils/branch-store.js";
5
5
  import { legacyConfigExists } from "../../utils/config.js";
6
+ import { MTIME_SOURCE_REASON_CODES } from "../../utils/content-freshness.js";
6
7
  import { readKbManifestStatus } from "../../utils/kb-manifest.js";
7
8
  import {
8
9
  getSchemaVersionStatus,
@@ -616,6 +617,9 @@ function readLegacySchemaVersion(
616
617
  }
617
618
 
618
619
  // implements REQ-agent-guided-migration-orchestration
620
+ /** Migration action code of the automatic recompile of changed sources. */
621
+ export const FRESHNESS_RESYNC = "freshness_resync";
622
+
619
623
  export function buildActionsFromStatus(input: {
620
624
  workspaceRoot: string;
621
625
  branchAttachment?: Readonly<Record<string, unknown>>;
@@ -777,10 +781,50 @@ export function buildActionsFromStatus(input: {
777
781
  );
778
782
  }
779
783
 
784
+ // implements REQ-cli-status-pre-first-sync
785
+ // Sources whose content changed since the last compilation (kb_status has
786
+ // already dropped mtime-only reasons) are fixed by recompiling them: one
787
+ // automatic `kibi sync`, not a review per file. An uncompiled store is
788
+ // compiled by the step above, which also covers these files.
789
+ const resyncReasons = (input.staleReasons ?? []).filter(
790
+ (reason) =>
791
+ typeof reason.code === "string" &&
792
+ MTIME_SOURCE_REASON_CODES.has(reason.code),
793
+ );
794
+ if (resyncReasons.length > 0 && uncompiled === undefined) {
795
+ actions.push(
796
+ migrationAction({
797
+ id: "freshness-resync",
798
+ code: FRESHNESS_RESYNC,
799
+ category: "freshness",
800
+ safety: "automatic",
801
+ invocation: { kind: "cli", command_argv: ["kibi", "sync"] },
802
+ affectedFiles: [
803
+ ...new Set(
804
+ resyncReasons.flatMap((reason) =>
805
+ typeof reason.path === "string" ? [reason.path] : [],
806
+ ),
807
+ ),
808
+ ].sort(),
809
+ evidence: { reasons: resyncReasons },
810
+ dependsOn: actions
811
+ .map((action) => action.id)
812
+ .filter(
813
+ (id) =>
814
+ id === "branch-store-ensure" || id === "schema-config-upgrade",
815
+ ),
816
+ postconditions: [{ syncState: "fresh" }],
817
+ autoApplicable: true,
818
+ }),
819
+ );
820
+ }
821
+
780
822
  for (const reason of input.staleReasons ?? []) {
781
823
  const code = typeof reason.code === "string" ? reason.code : "stale_source";
782
824
  // Planned above as an automatic compile step, not a review.
783
825
  if (code === BRANCH_STORE_NOT_COMPILED) continue;
826
+ // Planned above as one automatic resync (or covered by the compile).
827
+ if (MTIME_SOURCE_REASON_CODES.has(code)) continue;
784
828
  const file = typeof reason.path === "string" ? reason.path : "";
785
829
  actions.push(
786
830
  migrationAction({
@@ -2,6 +2,7 @@ import { execFileSync } from "node:child_process";
2
2
  import path from "node:path";
3
3
  import { discoverSourceFiles } from "../../commands/sync/discovery.js";
4
4
  import { processExtractions } from "../../commands/sync/extraction.js";
5
+ import { findUntrackedDocumentMatches } from "../../commands/sync/untracked-targets.js";
5
6
  import type { ExtractedRelationship } from "../../extractors/markdown.js";
6
7
  import { RELATIONSHIP_TYPES } from "../../operations/mutation/relationships.js";
7
8
  import { parseListOfLists, parsePrologValue } from "../../prolog/codec.js";
@@ -234,8 +235,64 @@ export async function collectSourceRelationshipParityViolations(
234
235
  });
235
236
  }
236
237
 
237
- return compareRelationshipParity(
238
- authored,
239
- await compiledRelationships(prolog),
238
+ return explainUntrackedEndpoints(
239
+ workspaceRoot,
240
+ compareRelationshipParity(authored, await compiledRelationships(prolog)),
240
241
  );
241
242
  }
243
+
244
+ /**
245
+ * An authored relationship is missing from the compiled KB most often
246
+ * because an endpoint is declared in a file sync cannot see: one Git does not
247
+ * track and no pending-source receipt covers (for example a `.kb/symbols.yaml`
248
+ * that `kibi init` created and kb_upsert appended to before receipts covered
249
+ * existing untracked files). Name that file and the fix.
250
+ */
251
+ // implements REQ-kibi-source-relationship-parity
252
+ function explainUntrackedEndpoints(
253
+ workspaceRoot: string,
254
+ violations: Violation[],
255
+ ): Violation[] {
256
+ const endpoints = new Set<string>();
257
+ for (const violation of violations) {
258
+ const relationship = authoredToCompiledRelationship(violation);
259
+ if (relationship === null) continue;
260
+ endpoints.add(relationship.from);
261
+ endpoints.add(relationship.to);
262
+ }
263
+ if (endpoints.size === 0) return violations;
264
+ const untracked = findUntrackedDocumentMatches(workspaceRoot, [...endpoints]);
265
+ if (untracked.size === 0) return violations;
266
+ return violations.map((violation) => {
267
+ const relationship = authoredToCompiledRelationship(violation);
268
+ if (relationship === null) return violation;
269
+ const sources = [relationship.from, relationship.to].flatMap((id) => {
270
+ const file = untracked.get(id);
271
+ return file === undefined ? [] : [{ entityId: id, file }];
272
+ });
273
+ if (sources.length === 0) return violation;
274
+ const files = [...new Set(sources.map((source) => source.file))];
275
+ return {
276
+ ...violation,
277
+ suggestion: `${sources.map((source) => source.entityId).join(" and ")} ${sources.length === 1 ? "is" : "are"} declared in ${files.join(", ")}, which Git does not track and no pending-source receipt covers, so sync never compiles it. Run 'git add ${files.join(" ")}' (staging is enough) or rewrite the entity with kb_upsert, then run kibi sync and rerun kb_check.`,
278
+ evidence: {
279
+ ...(violation.evidence ?? {}),
280
+ untrackedSources: sources,
281
+ },
282
+ };
283
+ });
284
+ }
285
+
286
+ function authoredToCompiledRelationship(
287
+ violation: Violation,
288
+ ): { from: string; to: string } | null {
289
+ const evidence = violation.evidence as
290
+ | { direction?: unknown; relationship?: { from?: unknown; to?: unknown } }
291
+ | undefined;
292
+ if (evidence?.direction !== "authored_to_compiled") return null;
293
+ const from = evidence.relationship?.from;
294
+ const to = evidence.relationship?.to;
295
+ return typeof from === "string" && typeof to === "string"
296
+ ? { from, to }
297
+ : null;
298
+ }
@@ -24,11 +24,22 @@ const ENTITY_TYPES = [
24
24
  "fact",
25
25
  ] as const;
26
26
 
27
+ const FACT_KINDS = [
28
+ "subject",
29
+ "property_value",
30
+ "predicate",
31
+ "predicate_schema",
32
+ "observation",
33
+ "meta",
34
+ "rule",
35
+ "rule_schema",
36
+ ] as const;
37
+
27
38
  export const querySpec = {
28
39
  name: "kb_query",
29
40
  cliName: "query",
30
41
  description:
31
- "Read entities from the KB with filters. Use for discovery and lookup before edits. Do not use for writes. No mutation side effects. Tags filter by metadata tags only, not entity IDs.",
42
+ "Read entities from the KB with exact filters, or ask typed fact questions. Use for lookup by id, type, tags or sourceFile, and for precise questions about modeled facts: predicate facts by name with named arguments (predicate + where), property facts of a subject (subjectKey + propertyKey), or the catalog of facts of one kind (factKind; 'predicate_schema' lists the predicates you can ask about with their argument names and constants). Typed answers return matching fact rows plus factQuery: the schema used, distinct counts, values that do exist when nothing matched, warnings about the question and the next call to make. Read-only. Tags filter by metadata tags only, not entity IDs.",
32
43
  businessInputSchema: {
33
44
  type: "object",
34
45
  properties: {
@@ -54,6 +65,61 @@ export const querySpec = {
54
65
  description:
55
66
  "Optional source-file substring filter. Example: 'src/auth/login.ts'. Uses KB source linkage, not file-system scanning.",
56
67
  },
68
+ factKind: {
69
+ type: "string",
70
+ enum: FACT_KINDS,
71
+ description:
72
+ "Typed fact filter: list facts of one kind with their typed fields. 'predicate_schema' returns the catalog of askable predicates (argument names, constants, fact counts). Example: 'predicate_schema'.",
73
+ },
74
+ predicate: {
75
+ type: "string",
76
+ description:
77
+ "Typed fact filter: predicate name to ask about, e.g. 'has_role' or 'permission_rule'. Combine with where for named arguments. Returns every stored fact of that predicate with its arguments, polarity and allow/deny effect.",
78
+ },
79
+ namespace: {
80
+ type: "string",
81
+ description:
82
+ "Optional predicate namespace when the same name exists in several. Default: any namespace.",
83
+ },
84
+ where: {
85
+ type: "object",
86
+ additionalProperties: { type: "string" },
87
+ description:
88
+ "Named argument values for predicate, order-free, using the argument names from the predicate's schema. Example: {actor: 'admin', decision: 'allow'}. An unknown name returns a warning with the declared names; no match returns the values that do exist.",
89
+ },
90
+ args: {
91
+ type: "array",
92
+ items: { type: ["string", "null"] },
93
+ description:
94
+ "Positional argument values for predicate when it has no schema; null matches any value. Example: ['admin', null, null, 'allow']. Prefer where.",
95
+ },
96
+ polarity: {
97
+ type: "string",
98
+ enum: ["assert", "deny"],
99
+ description:
100
+ "Optional polarity filter for predicate facts. Default: both.",
101
+ },
102
+ scope: {
103
+ type: "string",
104
+ enum: ["all", "current_requirements"],
105
+ description:
106
+ "Optional: 'current_requirements' keeps only facts a current (not superseded or retired) requirement requires.",
107
+ },
108
+ distinct: {
109
+ type: "string",
110
+ description:
111
+ "Optional argument name to count distinct values of across the matching predicate facts, e.g. distinct: 'role' with predicate 'has_role' and where {user: 'alice'} answers how many roles alice has.",
112
+ },
113
+ subjectKey: {
114
+ type: "string",
115
+ description:
116
+ "Typed fact filter: dotted subject key whose property facts to list, e.g. 'user.role_assignment'. Combine with propertyKey for one property.",
117
+ },
118
+ propertyKey: {
119
+ type: "string",
120
+ description:
121
+ "Optional property key under subjectKey, e.g. 'max_roles'. Returns operator, value and unit of each fact.",
122
+ },
57
123
  limit: {
58
124
  type: "number",
59
125
  default: 100,
@@ -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.13.0
5
+ version: 2.15.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -237,7 +237,11 @@ authored. Each one is the order the write path accepts.
237
237
  fact, `requires_predicate` to `relationshipTarget` and `specified_by` to
238
238
  its scenario: `specified_by` (req → scenario) is sent in the
239
239
  requirement's write, never in the scenario's, so create the scenario
240
- first. A
240
+ first, with `assumes` links (scenario → `property_value` fact, sent in
241
+ the scenario's own write) to the values its outcome relies on. Set
242
+ `expects` only on a scenario that has `assumes` links; `expects: success`
243
+ without them only yields `scenario-feasibility-unknown` ("assumes
244
+ nothing"). A
241
245
  `modeled` entry and its grounding link must arrive in the same write
242
246
  (`modeled semantic_inventory entries (n) must equal logical grounding
243
247
  relationships (m)`); a clause not grounded yet stays `status: missing`
@@ -249,7 +253,12 @@ authored. Each one is the order the write path accepts.
249
253
  claim_keys must match logical grounding target claim_keys exactly`.
250
254
  Prefer supersession for a change of meaning: analyze the new prose,
251
255
  write its facts, write the new requirement (`-v2`) with `supersedes` to
252
- the old one, then set the old one to `status: closed`. For a wording fix
256
+ the old one, then set the old one to `status: closed`. Its scenario
257
+ follows the same rule as in recipe 1: create the new scenario (with
258
+ `assumes` to the `property_value` facts it relies on, and `expects` only
259
+ when it has them) before the `-v2` write that sends `specified_by`, then
260
+ re-point the old scenario (its `assumes` go in its own write) or close
261
+ it. For a wording fix
253
262
  that keeps the meaning, re-ground in place: analyze the new prose,
254
263
  `kb_upsert` each grounding fact with the new `claim_key`/`claim_text`
255
264
  (same fact id), then `kb_upsert` the requirement with the new ledger,
@@ -400,6 +409,14 @@ Prolog `symbol-coverage` also requires the test to `validates` the requirement
400
409
  enough. Do not rely on legacy `// implements REQ-<area>-<behavior>` comments as the
401
410
  traceability record.
402
411
 
412
+ Set the symbol's `title` to the behavioral member the extractor can locate in
413
+ `sourceFile`: `ClassName.methodName`, an exported function, or a class, not a
414
+ file or module name. A traceability link (`implements`, `covered_by`, …) from
415
+ a symbol whose `title` names no such member is refused by the granularity gate
416
+ when the file already has granular behavioral symbols, unless the symbol sets
417
+ `granularity_reason` to `config-artifact`, `module-level-behavior`,
418
+ `extractor-miss`, `legacy-link` or `test-suite`.
419
+
403
420
  ## Complete Logical Coverage
404
421
 
405
422
  Decompose all atomic normative clauses into `claim_key`/`claim_text` entries in
@@ -97,6 +97,29 @@ Cosmetic edits (descriptions, labels) never stale proof. Execution-relevant
97
97
  edits (command, artifact, targets, options, bindings, contract) change the
98
98
  effective fingerprint and do.
99
99
 
100
+ ## What counts toward proofProven
101
+
102
+ A requirement is proven only by its scenarios' tests, and a scenario test
103
+ counts only when it is an **end-to-end** test with a valid, fresh, passing
104
+ receipt for the current snapshot.
105
+
106
+ - The scope comes from the test entity's `verification_scope`
107
+ (`end_to_end`, `integration`, `unit`), never from the integration id or the
108
+ producer. Without `verification_scope`, a test tagged `e2e` (or whose
109
+ `source` path contains `e2e`) counts as end-to-end; anything else is
110
+ `unknown` scope.
111
+ - A `unit` or `integration` receipt is still recorded on the test entity, but
112
+ it does not move `proofProven` for a behavioural requirement: the
113
+ requirement keeps the `missing_passing_e2e` gap in `proofGaps`. The one
114
+ exception is a scenario whose specifying requirements are all grounded only
115
+ in component-scope UI predicates (`ui_pattern`, `same_pattern`,
116
+ `pattern_marker`); a unit or integration test that renders the component
117
+ may prove it.
118
+ - `coverage_depth` (`kb_coverage`, `kb_find_gaps`) follows the test's
119
+ authored `status` (`passing`) and scope, not its receipts: a passing
120
+ receipt does not change it, and a `passing` status without a fresh receipt
121
+ proves nothing.
122
+
100
123
  ## Attempt semantics (factual, never optimistic)
101
124
 
102
125
  - `attempts: {status: "complete", entries: [...]}` — known history; the