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
@@ -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
@@ -1,3 +1,7 @@
1
+ /** Stale-reason code of a branch store that was never compiled. */
2
+ export declare const BRANCH_STORE_NOT_COMPILED = "branch_store_not_compiled";
3
+ /** The kb_check rule that reports it. */
4
+ export declare const BRANCH_STORE_NOT_COMPILED_RULE = "branch-store-not-compiled";
1
5
  export type BranchStoreState = "healthy" | "missing" | "incomplete" | "unreadable";
2
6
  export type BranchStoreInspection = Readonly<{
3
7
  state: BranchStoreState;
@@ -18,4 +22,32 @@ export declare function inspectBranchStore(workspaceRoot: string, branch: string
18
22
  */
19
23
  export declare function storeLockJournalReason(storePath: string): Record<string, unknown> | null;
20
24
  export declare function branchStoreReason(inspection: BranchStoreInspection): Record<string, unknown> | null;
25
+ /**
26
+ * The number of authored source files under `.kb/`: entity Markdown in the
27
+ * entity lanes plus relationship shards. Filesystem only, like
28
+ * inspectBranchStore, so a status read never compiles anything.
29
+ */
30
+ export declare function countAuthoredKbSources(workspaceRoot: string): number;
31
+ export type BranchStoreCompilation = Readonly<{
32
+ /** True once a sync (or any write) has committed into the store. */
33
+ compiled: boolean;
34
+ /** The journal CURRENT pointer (`generation-<id>:<sequence>`), if any. */
35
+ generation: string | null;
36
+ }>;
37
+ /**
38
+ * Whether the branch store holds a compilation. A missing store holds none;
39
+ * a journaled store whose CURRENT sequence is still 0 and whose journal holds
40
+ * no graph data was created (by `kibi branch ensure` or by an engine
41
+ * attaching to a new branch) but nothing was ever committed into it, so it is
42
+ * empty. A legacy kb.rdf store counts as compiled.
43
+ */
44
+ export declare function branchStoreCompilation(inspection: BranchStoreInspection): BranchStoreCompilation;
45
+ /**
46
+ * The single blocking reason for a branch whose store was never compiled
47
+ * while `.kb/` holds authored sources: Kibi stores are per branch and never
48
+ * copied, so a fresh branch (no post-checkout hook) starts empty. Every
49
+ * check against that store would report each authored relationship as
50
+ * missing; the one fix is `kibi sync`.
51
+ */
52
+ export declare function uncompiledBranchStoreReason(workspaceRoot: string, inspection: BranchStoreInspection, branch: string): Record<string, unknown> | null;
21
53
  //# sourceMappingURL=branch-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"branch-store.d.ts","sourceRoot":"","sources":["../../src/utils/branch-store.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,SAAS,GACT,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,KAAK,EAAE,gBAAgB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb,qBAAqB,CA0GvB;AAED;;;;GAIG;AAEH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAiBhC;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,qBAAqB,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAWhC"}
1
+ {"version":3,"file":"branch-store.d.ts","sourceRoot":"","sources":["../../src/utils/branch-store.ts"],"names":[],"mappings":"AAwBA,mEAAmE;AACnE,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AACrE,yCAAyC;AACzC,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAE1E,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,SAAS,GACT,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IAC3C,KAAK,EAAE,gBAAgB,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC,CAAC;AAEH;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb,qBAAqB,CA0GvB;AAED;;;;GAIG;AAEH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,GAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAiBhC;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,qBAAqB,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAWhC;AA6BD;;;;GAIG;AAEH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAOpE;AAED,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,oEAAoE;IACpE,QAAQ,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC,CAAC;AAEH;;;;;;GAMG;AAEH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,qBAAqB,GAChC,sBAAsB,CAwBxB;AAkBD;;;;;;GAMG;AAEH,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,qBAAqB,EACjC,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAwBhC"}
@@ -2,10 +2,15 @@
2
2
  Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
3
  Copyright (C) 2026 Piotr Franczyk
4
4
  */
5
- import { existsSync, readFileSync, statSync } from "node:fs";
5
+ import { existsSync, readFileSync, readdirSync, statSync, } from "node:fs";
6
6
  import * as path from "node:path";
7
7
  import { classifyStoreLockHolder, readStoreLockOwner, } from "../prolog/store-lock.js";
8
8
  import { branchStoreManifestMatches, branchStorePath, legacyBranchStorePath, } from "./branch-store-locator.js";
9
+ import { ENTITY_LANES, KB_ROOT } from "./kb-paths.js";
10
+ /** Stale-reason code of a branch store that was never compiled. */
11
+ export const BRANCH_STORE_NOT_COMPILED = "branch_store_not_compiled";
12
+ /** The kb_check rule that reports it. */
13
+ export const BRANCH_STORE_NOT_COMPILED_RULE = "branch-store-not-compiled";
9
14
  /**
10
15
  * Performs only filesystem checks. It deliberately never starts the engine or
11
16
  * attaches Prolog, so callers can still explain a broken branch store.
@@ -155,3 +160,119 @@ export function branchStoreReason(inspection) {
155
160
  : { command_argv: ["kibi", "branch", "ensure"], applyRequired: false },
156
161
  };
157
162
  }
163
+ /** Directories under .kb/ that hold authored, Git-tracked entity sources. */
164
+ const AUTHORED_SOURCE_DIRECTORIES = [
165
+ ...ENTITY_LANES.map((lane) => [lane, ".md"]),
166
+ ["adrs", ".md"],
167
+ ["relationships", ".yaml"],
168
+ ];
169
+ function countFiles(directory, extension) {
170
+ let count = 0;
171
+ let entries;
172
+ try {
173
+ entries = readdirSync(directory, { withFileTypes: true });
174
+ }
175
+ catch {
176
+ return 0;
177
+ }
178
+ for (const entry of entries) {
179
+ if (entry.isDirectory()) {
180
+ count += countFiles(path.join(directory, entry.name), extension);
181
+ }
182
+ else if (entry.isFile() && entry.name.endsWith(extension)) {
183
+ count += 1;
184
+ }
185
+ }
186
+ return count;
187
+ }
188
+ /**
189
+ * The number of authored source files under `.kb/`: entity Markdown in the
190
+ * entity lanes plus relationship shards. Filesystem only, like
191
+ * inspectBranchStore, so a status read never compiles anything.
192
+ */
193
+ // implements REQ-cli-status-pre-first-sync
194
+ export function countAuthoredKbSources(workspaceRoot) {
195
+ const kbRoot = path.join(workspaceRoot, KB_ROOT);
196
+ return AUTHORED_SOURCE_DIRECTORIES.reduce((total, [directory, extension]) => total + countFiles(path.join(kbRoot, directory), extension), 0);
197
+ }
198
+ /**
199
+ * Whether the branch store holds a compilation. A missing store holds none;
200
+ * a journaled store whose CURRENT sequence is still 0 and whose journal holds
201
+ * no graph data was created (by `kibi branch ensure` or by an engine
202
+ * attaching to a new branch) but nothing was ever committed into it, so it is
203
+ * empty. A legacy kb.rdf store counts as compiled.
204
+ */
205
+ // implements REQ-cli-status-pre-first-sync
206
+ export function branchStoreCompilation(inspection) {
207
+ if (inspection.state === "missing") {
208
+ return { compiled: false, generation: null };
209
+ }
210
+ const currentPath = path.join(inspection.path, "CURRENT");
211
+ let current = "";
212
+ try {
213
+ current = existsSync(currentPath)
214
+ ? readFileSync(currentPath, "utf8").trim()
215
+ : "";
216
+ }
217
+ catch {
218
+ current = "";
219
+ }
220
+ if (current === "") {
221
+ // A healthy store without CURRENT is a legacy kb.rdf store.
222
+ return { compiled: inspection.state === "healthy", generation: null };
223
+ }
224
+ const sequence = Number(current.split(":").at(-1));
225
+ return {
226
+ compiled: (Number.isFinite(sequence) && sequence > 0) ||
227
+ journalHoldsGraphData(inspection.path),
228
+ generation: current,
229
+ };
230
+ }
231
+ /**
232
+ * Whether the journal directory holds persisted graph data. An empty store
233
+ * has only `rdf/lock`; a store migrated from a legacy kb.rdf holds the
234
+ * imported graph under a fresh `generation-…:0` pointer, so it is compiled
235
+ * although its sequence is still 0.
236
+ */
237
+ function journalHoldsGraphData(storePath) {
238
+ try {
239
+ return readdirSync(path.join(storePath, "rdf")).some((entry) => entry !== "lock" && !entry.startsWith("."));
240
+ }
241
+ catch {
242
+ return false;
243
+ }
244
+ }
245
+ /**
246
+ * The single blocking reason for a branch whose store was never compiled
247
+ * while `.kb/` holds authored sources: Kibi stores are per branch and never
248
+ * copied, so a fresh branch (no post-checkout hook) starts empty. Every
249
+ * check against that store would report each authored relationship as
250
+ * missing; the one fix is `kibi sync`.
251
+ */
252
+ // implements REQ-cli-status-pre-first-sync
253
+ export function uncompiledBranchStoreReason(workspaceRoot, inspection, branch) {
254
+ if (inspection.state !== "healthy" && inspection.state !== "missing") {
255
+ return null;
256
+ }
257
+ const compilation = branchStoreCompilation(inspection);
258
+ if (compilation.compiled)
259
+ return null;
260
+ const authoredSources = countAuthoredKbSources(workspaceRoot);
261
+ if (authoredSources === 0)
262
+ return null;
263
+ const storeState = inspection.state === "missing"
264
+ ? "does not exist yet"
265
+ : `is empty (${compilation.generation ?? "no generation"}: nothing compiled)`;
266
+ return {
267
+ code: BRANCH_STORE_NOT_COMPILED,
268
+ path: inspection.path,
269
+ entityIds: [],
270
+ detail: `The KB store for branch ${branch} ${storeState}, while .kb/ holds ${authoredSources} authored source file(s). Kibi compiles one store per branch and never copies another branch's, so every query and check reads an empty KB until this branch is compiled.`,
271
+ remediation: { command_argv: ["kibi", "sync"], applyRequired: false },
272
+ blocking: true,
273
+ authoredSources,
274
+ ...(compilation.generation !== null
275
+ ? { generation: compilation.generation }
276
+ : {}),
277
+ };
278
+ }
@@ -23,6 +23,11 @@ export declare const GENERATED_RULES: readonly [{
23
23
  readonly description: "Authored Markdown and relationship-shard edges must exactly match the compiled KB";
24
24
  readonly enforcementClass: "canonical";
25
25
  readonly category: "integrity";
26
+ }, {
27
+ readonly name: "branch-store-not-compiled";
28
+ readonly description: "The current branch's KB store must be compiled from the authored .kb/ sources; a missing or empty store is reported once, naming kibi sync, instead of every store-backed rule reading an empty KB";
29
+ readonly enforcementClass: "canonical";
30
+ readonly category: "integrity";
26
31
  }, {
27
32
  readonly name: "source-path-dangling";
28
33
  readonly description: "An authored entity's source field must name an existing workspace path (a #anchor suffix is ignored), an existing entity id, or an http(s) URL; a missing source is allowed";
@@ -1 +1 @@
1
- {"version":3,"file":"rule-registry.generated.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.generated.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqUlB,CAAC"}
1
+ {"version":3,"file":"rule-registry.generated.d.ts","sourceRoot":"","sources":["../../src/utils/rule-registry.generated.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4UlB,CAAC"}
@@ -38,6 +38,12 @@ export const GENERATED_RULES = [
38
38
  enforcementClass: "canonical",
39
39
  category: "integrity",
40
40
  },
41
+ {
42
+ name: "branch-store-not-compiled",
43
+ description: "The current branch's KB store must be compiled from the authored .kb/ sources; a missing or empty store is reported once, naming kibi sync, instead of every store-backed rule reading an empty KB",
44
+ enforcementClass: "canonical",
45
+ category: "integrity",
46
+ },
41
47
  {
42
48
  name: "source-path-dangling",
43
49
  description: "An authored entity's source field must name an existing workspace path (a #anchor suffix is ignored), an existing entity id, or an http(s) URL; a missing source is allowed",
@@ -12,6 +12,12 @@ export interface SemanticClaim {
12
12
  export interface StrictModelInput {
13
13
  claim: SemanticClaim;
14
14
  statement: string;
15
+ /**
16
+ * The requirement the write set updates. When set it replaces the minted
17
+ * REQ-AUTO-<hash> id; fact ids, subject_key and the claim stay derived from
18
+ * the claim.
19
+ */
20
+ requirementId?: string;
15
21
  }
16
22
  type EntityProperties = Partial<Omit<BaseEntity, "created_at" | "updated_at"> & FactFields> & {
17
23
  id: string;
@@ -65,5 +71,11 @@ export declare function buildStrictWriteSet(input: StrictModelInput): StrictWrit
65
71
  export declare function modelRequirementClaims(inputs: ReadonlyArray<StrictModelInput>): StrictWriteSet[];
66
72
  export declare function normalizeSourceKey(value: string): string;
67
73
  export declare function normalizeTextRef(provenance: string | undefined, source: string): string;
74
+ /**
75
+ * The file an authored fact lives in: `.kb/facts/<id>.md`. Kibi writes every
76
+ * fact there (the filename stem equals the id), so a plan that points at an
77
+ * existing fact names this path.
78
+ */
79
+ export declare function factSourcePath(factId: string): string;
68
80
  export {};
69
81
  //# sourceMappingURL=strict-modeling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"strict-modeling.d.ts","sourceRoot":"","sources":["../../src/utils/strict-modeling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"strict-modeling.d.ts","sourceRoot":"","sources":["../../src/utils/strict-modeling.ts"],"names":[],"mappings":"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"}
@@ -83,7 +83,9 @@ export function buildStableRequirementIds(claim) {
83
83
  export function buildStrictWriteSet(input) {
84
84
  const statement = normalizeStatement(input.statement);
85
85
  const confidence = normalizeConfidence(input.claim.confidence);
86
- const ids = buildStableRequirementIds(input.claim);
86
+ const minted = buildStableRequirementIds(input.claim);
87
+ const requirementId = input.requirementId?.trim();
88
+ const ids = requirementId ? { ...minted, reqId: requirementId } : minted;
87
89
  const textRef = normalizeTextRef(input.claim.provenance, input.claim.source);
88
90
  const metadataTags = buildMetadataTags({
89
91
  confidence,
@@ -409,3 +411,12 @@ function normalizeEntityDirectory(directory) {
409
411
  function buildEntitySourcePath(directory, entityId) {
410
412
  return `${directory}/${entityId}.md`;
411
413
  }
414
+ /**
415
+ * The file an authored fact lives in: `.kb/facts/<id>.md`. Kibi writes every
416
+ * fact there (the filename stem equals the id), so a plan that points at an
417
+ * existing fact names this path.
418
+ */
419
+ // implements REQ-kibi-subject-vocabulary
420
+ export function factSourcePath(factId) {
421
+ return buildEntitySourcePath(resolveEntitySourcePaths().facts, factId);
422
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cli",
3
- "version": "2.15.0",
3
+ "version": "2.17.0",
4
4
  "type": "module",
5
5
  "description": "Kibi CLI for knowledge base management",
6
6
  "engines": {
@@ -235,7 +235,7 @@
235
235
  "fast-glob": "^3.2.12",
236
236
  "gray-matter": "^4.0.3",
237
237
  "js-yaml": "^4.1.0",
238
- "kibi-core": "^0.18.0",
238
+ "kibi-core": "^0.18.2",
239
239
  "kibi-plugin-builtin": "^0.5.0",
240
240
  "kibi-plugin-sdk": "^0.5.0",
241
241
  "kibi-swipl": "^1.0.1",
@@ -276,6 +276,46 @@ function coverageForRequirement(
276
276
  };
277
277
  }
278
278
 
279
+ /**
280
+ * Requirement statuses that keep a requirement in the coverage-depth review.
281
+ * `closed` is reviewed only when tagged `implemented` (the documented
282
+ * "done, with evidence" shape); a plain closed, deprecated or superseded
283
+ * requirement is retired history, not a coverage gap.
284
+ */
285
+ const REVIEWED_REQUIREMENT_STATUSES = new Set([
286
+ "open",
287
+ "in_progress",
288
+ "active",
289
+ "approved",
290
+ ]);
291
+
292
+ function supersededRequirementIds(
293
+ manifestResults: readonly ExtractionResult[],
294
+ ): ReadonlySet<string> {
295
+ return new Set(
296
+ manifestResults.flatMap((result) =>
297
+ result.entity.type === "req"
298
+ ? result.relationships.flatMap((relationship) =>
299
+ relationship.type === "supersedes" ? [relationship.to] : [],
300
+ )
301
+ : [],
302
+ ),
303
+ );
304
+ }
305
+
306
+ // implements REQ-audit-quality-diagnostics-v1
307
+ function isReviewableRequirement(
308
+ requirement: ExtractedEntity,
309
+ superseded: ReadonlySet<string>,
310
+ ): boolean {
311
+ if (superseded.has(requirement.id)) return false;
312
+ const status = requirement.status.trim().toLowerCase();
313
+ if (REVIEWED_REQUIREMENT_STATUSES.has(status)) return true;
314
+ return (
315
+ status === "closed" && (requirement.tags ?? []).includes("implemented")
316
+ );
317
+ }
318
+
279
319
  export function createCoverageDepthQualityDiagnostics(
280
320
  manifestResults: readonly ExtractionResult[],
281
321
  proofByRequirement: ReadonlyMap<
@@ -289,8 +329,13 @@ export function createCoverageDepthQualityDiagnostics(
289
329
  > = new Map(),
290
330
  ): readonly QualityDiagnostic[] {
291
331
  const entities = entitiesById(manifestResults);
332
+ const superseded = supersededRequirementIds(manifestResults);
292
333
  return manifestResults
293
- .filter((result) => result.entity.type === "req")
334
+ .filter(
335
+ (result) =>
336
+ result.entity.type === "req" &&
337
+ isReviewableRequirement(result.entity, superseded),
338
+ )
294
339
  .flatMap((result) => {
295
340
  const diagnostic = createDiagnostic(
296
341
  coverageForRequirement(result, manifestResults, entities),
@@ -1,4 +1,10 @@
1
1
  import { runAggregatedChecks } from "../../commands/aggregated-checks.js";
2
+ import { resolveBranchAttachment } from "../../utils/branch-resolver.js";
3
+ import {
4
+ BRANCH_STORE_NOT_COMPILED_RULE,
5
+ inspectBranchStore,
6
+ uncompiledBranchStoreReason,
7
+ } from "../../utils/branch-store.js";
2
8
  /*
3
9
  Kibi — repo-local, per-branch, queryable long-term memory for software projects
4
10
  Copyright (C) 2026 Piotr Franczyk
@@ -74,6 +80,14 @@ export {
74
80
 
75
81
  export type CheckExecutionOptions = {
76
82
  readonly collectFullQualityDiagnosticsForExplicitRules?: boolean;
83
+ /**
84
+ * The engine reads the current branch's store although the context carries
85
+ * no branch attachment (the `kibi check` engine path). A context with a
86
+ * branch attachment (the CLI and MCP runtimes) implies it; without either,
87
+ * the attached store is unknown (an explicit --kb-path store or an injected
88
+ * test store) and the branch-store compilation check is skipped.
89
+ */
90
+ readonly branchStoreAttached?: boolean;
77
91
  };
78
92
 
79
93
  function requireProlog(context: OperationContext): PrologPort {
@@ -126,6 +140,50 @@ async function readStatusMigrationPlan(
126
140
  }
127
141
  }
128
142
 
143
+ /**
144
+ * The one finding for a branch whose store was never compiled while .kb/
145
+ * holds authored sources. Every store-backed rule would read an empty KB
146
+ * (source-relationship-parity alone reports each authored relationship as
147
+ * missing), so kb_check reports this instead of any of them.
148
+ */
149
+ // implements REQ-cli-status-pre-first-sync
150
+ export function uncompiledBranchStoreViolation(
151
+ context: OperationContext,
152
+ workspaceRoot: string,
153
+ ): Violation | null {
154
+ let attachment: ReturnType<typeof resolveBranchAttachment>;
155
+ try {
156
+ attachment =
157
+ context.branchAttachment ?? resolveBranchAttachment(workspaceRoot);
158
+ } catch {
159
+ return null;
160
+ }
161
+ if ("error" in attachment) return null;
162
+ const store = inspectBranchStore(workspaceRoot, attachment.kbBranch);
163
+ const reason = uncompiledBranchStoreReason(
164
+ workspaceRoot,
165
+ store,
166
+ attachment.kbBranch,
167
+ );
168
+ if (reason === null) return null;
169
+ return {
170
+ rule: BRANCH_STORE_NOT_COMPILED_RULE,
171
+ entityId: attachment.kbBranch,
172
+ description: String(reason.detail),
173
+ suggestion:
174
+ "Run kibi sync to compile this branch's store from the authored .kb/ sources (or apply the branch-store-compile action of the returned migrationPlan with kb_apply_plan), then rerun kb_check. Run kibi init once to install the git hooks that compile a new branch on checkout.",
175
+ source: store.path,
176
+ evidence: {
177
+ store: { state: store.state, path: store.path },
178
+ authoredSources: reason.authoredSources,
179
+ ...(reason.generation !== undefined
180
+ ? { generation: reason.generation }
181
+ : {}),
182
+ remediation: reason.remediation,
183
+ },
184
+ };
185
+ }
186
+
129
187
  // implements REQ-kibi-operation-interface-parity, REQ-002
130
188
  export async function executeCheck(
131
189
  args: CheckInput,
@@ -154,6 +212,53 @@ export async function executeCheck(
154
212
  ? { maxDiagnostics: args.maxDiagnostics }
155
213
  : {};
156
214
 
215
+ const uncompiled =
216
+ context.branchAttachment !== undefined ||
217
+ options.branchStoreAttached === true
218
+ ? uncompiledBranchStoreViolation(context, workspaceRoot)
219
+ : null;
220
+ if (uncompiled !== null) {
221
+ const violations = [uncompiled];
222
+ const statusPlan = await readStatusMigrationPlan(context);
223
+ const migrationPlan = await migrationPlanForCheck(
224
+ context,
225
+ violations,
226
+ [],
227
+ statusPlan,
228
+ );
229
+ return {
230
+ content: [
231
+ {
232
+ type: "text",
233
+ text: buildSummary({
234
+ violations,
235
+ impactResult,
236
+ qualityDiagnostics: [],
237
+ }),
238
+ },
239
+ ],
240
+ structuredContent: buildStructuredContent({
241
+ violations,
242
+ diagnostics: [
243
+ {
244
+ category: "SYNC_ERROR",
245
+ severity: "error",
246
+ message: uncompiled.description,
247
+ ...(uncompiled.source !== undefined
248
+ ? { file: uncompiled.source }
249
+ : {}),
250
+ ...(uncompiled.suggestion !== undefined
251
+ ? { suggestion: uncompiled.suggestion }
252
+ : {}),
253
+ },
254
+ ],
255
+ qualityDiagnostics: [],
256
+ impactResult,
257
+ migrationPlan,
258
+ }),
259
+ };
260
+ }
261
+
157
262
  if (rulesAllowlist.size === 0) {
158
263
  const qualityDiagnostics =
159
264
  hasExplicitRules &&