kibi-cli 2.26.3 → 2.27.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.
- package/dist/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +24 -0
- package/dist/commands/sync/persistence.d.ts +10 -0
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +24 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +5 -1
- package/dist/diagnostics.d.ts +6 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +25 -9
- package/dist/intent-search.d.ts +2 -0
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +5 -0
- package/dist/operations/bootstrap/activation.d.ts +6 -0
- package/dist/operations/bootstrap/activation.d.ts.map +1 -1
- package/dist/operations/bootstrap/activation.js +22 -0
- package/dist/operations/bootstrap/claim-restatement.d.ts +2 -0
- package/dist/operations/bootstrap/claim-restatement.d.ts.map +1 -1
- package/dist/operations/bootstrap/conflict-review.d.ts +16 -0
- package/dist/operations/bootstrap/conflict-review.d.ts.map +1 -0
- package/dist/operations/bootstrap/conflict-review.js +59 -0
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +17 -4
- package/dist/operations/bootstrap/types.d.ts +5 -0
- package/dist/operations/bootstrap/types.d.ts.map +1 -1
- package/dist/operations/migration/entity-type.d.ts +35 -0
- package/dist/operations/migration/entity-type.d.ts.map +1 -0
- package/dist/operations/migration/entity-type.js +89 -0
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +32 -0
- package/dist/operations/mutation/symbol-granularity.d.ts +1 -1
- package/dist/operations/mutation/symbol-granularity.d.ts.map +1 -1
- package/dist/operations/mutation/symbol-granularity.js +37 -7
- package/dist/operations/mutation/upsert.d.ts +9 -1
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +43 -7
- package/dist/operations/mutation/validation.d.ts.map +1 -1
- package/dist/operations/mutation/validation.js +21 -0
- package/dist/operations/planning/compile-intent.d.ts +16 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +118 -5
- package/dist/operations/planning/conflict-review.d.ts +69 -0
- package/dist/operations/planning/conflict-review.d.ts.map +1 -0
- package/dist/operations/planning/conflict-review.js +133 -0
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts +8 -0
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/ingestion-boundary.js +43 -2
- package/dist/public/impact/analyzer.d.ts.map +1 -1
- package/dist/public/impact/analyzer.js +26 -2
- package/dist/public/impact/requirement-statements.d.ts +29 -0
- package/dist/public/impact/requirement-statements.d.ts.map +1 -0
- package/dist/public/impact/requirement-statements.js +127 -0
- package/dist/public/operations/catalog.d.ts +26 -2
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-format-shared.d.ts +1 -1
- package/dist/public/operations/check-format-shared.d.ts.map +1 -1
- package/dist/public/operations/check-format-shared.js +19 -4
- package/dist/public/operations/discovery-executors.d.ts.map +1 -1
- package/dist/public/operations/discovery-executors.js +20 -13
- package/dist/public/operations/migration-plan.d.ts.map +1 -1
- package/dist/public/operations/migration-plan.js +7 -2
- package/dist/public/operations/result-envelope.d.ts +2 -0
- package/dist/public/operations/result-envelope.d.ts.map +1 -1
- package/dist/public/operations/result-envelope.js +21 -9
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/check.js +1 -1
- package/dist/public/operations/specs/planning.d.ts +25 -1
- package/dist/public/operations/specs/planning.d.ts.map +1 -1
- package/dist/public/operations/specs/planning.js +16 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/dist/public/skills/kibi-usage/SKILL.md +11 -1
- package/dist/traceability/staged-diagnostics.d.ts +1 -1
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/utils/config.d.ts +8 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +10 -0
- package/package.json +2 -2
- package/src/public/impact/analyzer.ts +38 -2
- package/src/public/impact/requirement-statements.ts +171 -0
- package/src/public/operations/check-format-shared.ts +27 -4
- package/src/public/operations/discovery-executors.ts +20 -13
- package/src/public/operations/migration-plan.ts +12 -4
- package/src/public/operations/result-envelope.ts +18 -4
- package/src/public/operations/specs/check.ts +1 -1
- package/src/public/operations/specs/planning.ts +17 -1
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/src/public/skills/kibi-usage/SKILL.md +11 -1
|
@@ -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.
|
|
5
|
+
version: 2.20.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -410,6 +410,16 @@ Conditional clauses ("only when", "only if", "must not ... unless") compile
|
|
|
410
410
|
into typed `forbid` rules linked by `requires_rule`, not property facts; one
|
|
411
411
|
`kb_model` or `kb_compile_intent` cannot translate stays an `ontology_gap`.
|
|
412
412
|
|
|
413
|
+
`kb_compile_intent` reports `contradictionAnalysis.outcome: no_conflict` only
|
|
414
|
+
when it could check: Prolog compares only requirements with logic links. When
|
|
415
|
+
the intent is unmodeled or a related current requirement is prose only, the
|
|
416
|
+
outcome is `unresolved` with `reasons` and up to five `reviewCandidates`. Never
|
|
417
|
+
read `unresolved` as consistent. Compare the intent with each candidate's
|
|
418
|
+
`statement`, then compile again with `conflictReviews`
|
|
419
|
+
(`{requirementId, verdict: "conflict" | "no_conflict", note}`). On a conflict
|
|
420
|
+
the plan is `blocked`: ask the human which rule holds (a firm requirement is
|
|
421
|
+
theirs), then supersede or change it, or change the intent.
|
|
422
|
+
|
|
413
423
|
## Semantic and proof guardrails
|
|
414
424
|
|
|
415
425
|
Prolog proves only encoded facts. Keep ambiguity, ontology gaps, incomplete
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { QualityDiagnostic } from "../public/impact/types.js";
|
|
2
2
|
import { type KibiImpactEvidence } from "./evidence-model.js";
|
|
3
|
-
export type KibiImpactDiagnosticId = "kibi_impact_evidence_missing" | "symbols_manifest_stale" | "symbol_granularity_violation" | "symbol_semantic_review_needed" | "source_file_unowned" | "multi_requirement_symbol_review" | "duplicate_symbol_coordinate_review" | "component_mixed_purpose_review" | "broad_requirement_review" | "requirement_status_review" | "strict_fact_modeling_review" | "logical_coverage_review" | "kibi_impact_override_missing_rationale" | "entity_id_style_review";
|
|
3
|
+
export type KibiImpactDiagnosticId = "kibi_impact_evidence_missing" | "symbols_manifest_stale" | "symbol_granularity_violation" | "symbol_semantic_review_needed" | "source_file_unowned" | "requirement_statement_unverified" | "multi_requirement_symbol_review" | "duplicate_symbol_coordinate_review" | "component_mixed_purpose_review" | "broad_requirement_review" | "requirement_status_review" | "strict_fact_modeling_review" | "logical_coverage_review" | "kibi_impact_override_missing_rationale" | "entity_id_style_review";
|
|
4
4
|
export interface KibiImpactDiagnostic extends QualityDiagnostic {
|
|
5
5
|
/** Stable staged-enforcement diagnostic identifier. */
|
|
6
6
|
id: KibiImpactDiagnosticId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"staged-diagnostics.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAGL,KAAK,kBAAkB,EAKxB,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,MAAM,sBAAsB,GAC9B,8BAA8B,GAC9B,wBAAwB,GACxB,8BAA8B,GAC9B,+BAA+B,GAC/B,qBAAqB,GACrB,iCAAiC,GACjC,oCAAoC,GACpC,gCAAgC,GAChC,0BAA0B,GAC1B,2BAA2B,GAC3B,6BAA6B,GAC7B,yBAAyB,GACzB,wCAAwC,GACxC,wBAAwB,CAAC;AAE7B,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,uDAAuD;IACvD,EAAE,EAAE,sBAAsB,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACxC,iEAAiE;IACjE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,gDAAgD;IAChD,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAsID,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,kBAAkB,GAC3B,oBAAoB,CAoBtB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,kBAAkB,EAC5B,mBAAmB,GAAE,MAAmC,GACvD,oBAAoB,EAAE,CAiDxB"}
|
|
1
|
+
{"version":3,"file":"staged-diagnostics.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAGL,KAAK,kBAAkB,EAKxB,MAAM,qBAAqB,CAAC;AAQ7B,MAAM,MAAM,sBAAsB,GAC9B,8BAA8B,GAC9B,wBAAwB,GACxB,8BAA8B,GAC9B,+BAA+B,GAC/B,qBAAqB,GACrB,kCAAkC,GAClC,iCAAiC,GACjC,oCAAoC,GACpC,gCAAgC,GAChC,0BAA0B,GAC1B,2BAA2B,GAC3B,6BAA6B,GAC7B,yBAAyB,GACzB,wCAAwC,GACxC,wBAAwB,CAAC;AAE7B,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,uDAAuD;IACvD,EAAE,EAAE,sBAAsB,CAAC;IAC3B,QAAQ,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;IACxC,iEAAiE;IACjE,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,gDAAgD;IAChD,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAsID,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,kBAAkB,GAC3B,oBAAoB,CAoBtB;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,kBAAkB,EAC5B,mBAAmB,GAAE,MAAmC,GACvD,oBAAoB,EAAE,CAiDxB"}
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -35,6 +35,14 @@ export interface LegacyKbConfig {
|
|
|
35
35
|
checks?: unknown;
|
|
36
36
|
defaultBranch?: string;
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The first step on a workspace with a legacy `.kb/config.json` and no
|
|
40
|
+
* manifest. Doctor and kb_status both name it, so they never disagree on
|
|
41
|
+
* what to run first: `kibi init` would claim the latest schema and skip the
|
|
42
|
+
* legacy migration steps.
|
|
43
|
+
*/
|
|
44
|
+
export declare const LEGACY_KB_MIGRATION_COMMAND = "kibi migrate --yes";
|
|
45
|
+
export declare const LEGACY_KB_MIGRATION_NOTE = "it creates the manifest from the legacy .kb/config.json; do not run kibi init first";
|
|
38
46
|
/** Whether a legacy `.kb/config.json` exists at the workspace root. */
|
|
39
47
|
export declare function legacyConfigExists(cwd: string): boolean;
|
|
40
48
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAEvB;;;;;;GAMG;AAEH,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,EAAE,aAAsC,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAsB,GAAG,aAAa,CAE3E;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,gBAAgB,CAAC;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAcD,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAET;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAqE9C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,aAAa,CAwBf"}
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AAEvB;;;;;;GAMG;AAEH,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B,6EAA6E;AAC7E,eAAO,MAAM,oBAAoB,EAAE,aAAsC,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAsB,GAAG,aAAa,CAE3E;AAED,6EAA6E;AAC7E,MAAM,WAAW,mBAAmB;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAC5B,wDAAwD;IACxD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAChC,uBAAuB,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,gBAAgB,CAAC;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAcD;;;;;GAKG;AAEH,eAAO,MAAM,2BAA2B,uBAAuB,CAAC;AAEhE,eAAO,MAAM,wBAAwB,wFACkD,CAAC;AAExF,uEAAuE;AACvE,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAET;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAqE9C;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,aAAa,CAwBf"}
|
package/dist/utils/config.js
CHANGED
|
@@ -34,6 +34,16 @@ function pickLegacyPathValue(paths, key) {
|
|
|
34
34
|
const value = paths[key];
|
|
35
35
|
return typeof value === "string" ? value : undefined;
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* The first step on a workspace with a legacy `.kb/config.json` and no
|
|
39
|
+
* manifest. Doctor and kb_status both name it, so they never disagree on
|
|
40
|
+
* what to run first: `kibi init` would claim the latest schema and skip the
|
|
41
|
+
* legacy migration steps.
|
|
42
|
+
*/
|
|
43
|
+
// implements REQ-legacy-kb-first-step-migrate
|
|
44
|
+
export const LEGACY_KB_MIGRATION_COMMAND = "kibi migrate --yes";
|
|
45
|
+
// implements REQ-legacy-kb-first-step-migrate
|
|
46
|
+
export const LEGACY_KB_MIGRATION_NOTE = "it creates the manifest from the legacy .kb/config.json; do not run kibi init first";
|
|
37
47
|
/** Whether a legacy `.kb/config.json` exists at the workspace root. */
|
|
38
48
|
export function legacyConfigExists(cwd) {
|
|
39
49
|
return existsSync(path.join(cwd, ".kb", "config.json"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.27.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.22.
|
|
238
|
+
"kibi-core": "^0.22.3",
|
|
239
239
|
"kibi-plugin-builtin": "^0.5.2",
|
|
240
240
|
"kibi-plugin-sdk": "^0.5.0",
|
|
241
241
|
"kibi-swipl": "^1.0.1",
|
|
@@ -20,6 +20,11 @@ import {
|
|
|
20
20
|
createImpactManifestLookup,
|
|
21
21
|
readImpactManifestResults,
|
|
22
22
|
} from "./manifest.js";
|
|
23
|
+
import {
|
|
24
|
+
createRequirementStatementDiagnostics,
|
|
25
|
+
readWorkspaceRequirement,
|
|
26
|
+
workspaceFileChanged,
|
|
27
|
+
} from "./requirement-statements.js";
|
|
23
28
|
import { normalizeSourceFile } from "./source-changes.js";
|
|
24
29
|
import { collectSourceChanges, uniqueSorted } from "./source-changes.js";
|
|
25
30
|
import {
|
|
@@ -34,6 +39,14 @@ import type {
|
|
|
34
39
|
} from "./types.js";
|
|
35
40
|
import { createUnownedFileDiagnostics } from "./unowned-files.js";
|
|
36
41
|
|
|
42
|
+
/** The workspace-relative document a requirement was read from. */
|
|
43
|
+
function requirementSourcePath(requirement: { id: string; source: string }) {
|
|
44
|
+
const source = requirement.source;
|
|
45
|
+
return source && !source.includes("://")
|
|
46
|
+
? source.replace(/^\.\//, "")
|
|
47
|
+
: `.kb/requirements/${requirement.id}.md`;
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
export async function analyzeChangedFileImpact(
|
|
38
51
|
options: ChangedFileImpactOptions,
|
|
39
52
|
): Promise<ChangedFileImpactResult> {
|
|
@@ -88,12 +101,15 @@ export async function analyzeChangedFileImpact(
|
|
|
88
101
|
.map((file) => file.path)
|
|
89
102
|
: sourceChanges.map((change) => change.file),
|
|
90
103
|
);
|
|
91
|
-
const
|
|
104
|
+
const snapshotKnowledge = snapshot
|
|
92
105
|
? readSnapshotKnowledge(
|
|
93
106
|
snapshot.readGit,
|
|
94
107
|
snapshot.headTree,
|
|
95
108
|
snapshot.readBlobs,
|
|
96
|
-
)
|
|
109
|
+
)
|
|
110
|
+
: undefined;
|
|
111
|
+
const manifestResults = snapshotKnowledge
|
|
112
|
+
? snapshotKnowledge.filter((result) => result.entity.type === "symbol")
|
|
97
113
|
: readImpactManifestResults(workspaceRoot);
|
|
98
114
|
const manifestLookup = createImpactManifestLookup(manifestResults);
|
|
99
115
|
const symbolsByFile = new Map<string, ExtractedSymbol[]>();
|
|
@@ -161,6 +177,26 @@ export async function analyzeChangedFileImpact(
|
|
|
161
177
|
requestedFiles: [...filter],
|
|
162
178
|
linkedSourceFiles,
|
|
163
179
|
}),
|
|
180
|
+
// What the changed code is meant to do, quoted, before the
|
|
181
|
+
// structural findings: "no violations" never compared it.
|
|
182
|
+
...createRequirementStatementDiagnostics({
|
|
183
|
+
workspaceRoot,
|
|
184
|
+
symbolsByFile,
|
|
185
|
+
readRequirement: (id) =>
|
|
186
|
+
snapshotKnowledge
|
|
187
|
+
? snapshotKnowledge.find((result) => result.entity.id === id)
|
|
188
|
+
?.entity
|
|
189
|
+
: readWorkspaceRequirement(workspaceRoot, id),
|
|
190
|
+
requirementChanged: (requirement) =>
|
|
191
|
+
snapshot
|
|
192
|
+
? snapshot.inventory.some(
|
|
193
|
+
(file) => file.path === requirementSourcePath(requirement),
|
|
194
|
+
)
|
|
195
|
+
: workspaceFileChanged(
|
|
196
|
+
workspaceRoot,
|
|
197
|
+
requirementSourcePath(requirement),
|
|
198
|
+
),
|
|
199
|
+
}),
|
|
164
200
|
...createSymbolGranularityDiagnostics({
|
|
165
201
|
manifestResults: activeManifestResults,
|
|
166
202
|
symbolsByFile,
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { effectiveFirmness } from "../../decision-firmness.js";
|
|
4
|
+
import {
|
|
5
|
+
type ExtractedEntity,
|
|
6
|
+
extractFromMarkdown,
|
|
7
|
+
} from "../../extractors/markdown.js";
|
|
8
|
+
import type { KibiImpactDiagnostic } from "../../traceability/staged-diagnostics.js";
|
|
9
|
+
import type { ExtractedSymbol } from "../../traceability/symbol-extract.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Kibi's checks compare structure (links, coverage, facts), never code with a
|
|
13
|
+
* requirement's prose. A change to code that implements a requirement can
|
|
14
|
+
* contradict it and still pass every rule, so the check says which
|
|
15
|
+
* requirements the changed code implements and quotes what each one says,
|
|
16
|
+
* instead of letting "no violations" read as "the change keeps them".
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const MAX_STATEMENT = 400;
|
|
20
|
+
const MAX_NAMED_SYMBOLS = 4;
|
|
21
|
+
const RETIRED_STATUS =
|
|
22
|
+
/supersed|deprecat|reject|obsolete|retired|archived|removed|closed/i;
|
|
23
|
+
|
|
24
|
+
// implements REQ-kb-check-unverified-requirement-statements
|
|
25
|
+
export type RequirementStatementDiagnosticsOptions = {
|
|
26
|
+
readonly workspaceRoot: string;
|
|
27
|
+
readonly symbolsByFile: ReadonlyMap<string, readonly ExtractedSymbol[]>;
|
|
28
|
+
/** The requirement entity for an id, or undefined when it is unknown. */
|
|
29
|
+
readonly readRequirement: (id: string) => ExtractedEntity | undefined;
|
|
30
|
+
/** Whether the requirement's document changed in this change. */
|
|
31
|
+
readonly requirementChanged: (requirement: ExtractedEntity) => boolean;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function implementedRequirementIds(symbol: ExtractedSymbol): string[] {
|
|
35
|
+
return [
|
|
36
|
+
...(symbol.relationships ?? [])
|
|
37
|
+
.filter((relationship) => relationship.type === "implements")
|
|
38
|
+
.map((relationship) => relationship.to),
|
|
39
|
+
...symbol.reqLinks,
|
|
40
|
+
].filter((id) => id.startsWith("REQ-"));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function truncate(text: string, limit: number): string {
|
|
44
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
45
|
+
return flat.length > limit ? `${flat.slice(0, limit - 1)}…` : flat;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// implements REQ-kb-check-unverified-requirement-statements
|
|
49
|
+
/**
|
|
50
|
+
* One `requirement_statement_unverified` warning per current requirement that
|
|
51
|
+
* a changed behavioral symbol implements, quoting the requirement's statement
|
|
52
|
+
* and firmness. A requirement whose own document changed in the same change
|
|
53
|
+
* is left to the firmness and supersede checks.
|
|
54
|
+
*/
|
|
55
|
+
export function createRequirementStatementDiagnostics(
|
|
56
|
+
options: RequirementStatementDiagnosticsOptions,
|
|
57
|
+
): KibiImpactDiagnostic[] {
|
|
58
|
+
const symbolsByRequirement = new Map<
|
|
59
|
+
string,
|
|
60
|
+
{ names: Set<string>; files: Set<string> }
|
|
61
|
+
>();
|
|
62
|
+
for (const symbols of options.symbolsByFile.values()) {
|
|
63
|
+
for (const symbol of symbols) {
|
|
64
|
+
if (symbol.role !== "behavioral" || symbol.hunkRanges.length === 0)
|
|
65
|
+
continue;
|
|
66
|
+
for (const id of implementedRequirementIds(symbol)) {
|
|
67
|
+
const entry = symbolsByRequirement.get(id) ?? {
|
|
68
|
+
names: new Set<string>(),
|
|
69
|
+
files: new Set<string>(),
|
|
70
|
+
};
|
|
71
|
+
entry.names.add(symbol.name);
|
|
72
|
+
entry.files.add(symbol.location.file);
|
|
73
|
+
symbolsByRequirement.set(id, entry);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const diagnostics: KibiImpactDiagnostic[] = [];
|
|
79
|
+
for (const id of [...symbolsByRequirement.keys()].sort()) {
|
|
80
|
+
const requirement = options.readRequirement(id);
|
|
81
|
+
if (requirement === undefined || requirement.type !== "req") continue;
|
|
82
|
+
if (RETIRED_STATUS.test(requirement.status ?? "")) continue;
|
|
83
|
+
if (options.requirementChanged(requirement)) continue;
|
|
84
|
+
const entry = symbolsByRequirement.get(id);
|
|
85
|
+
if (entry === undefined) continue;
|
|
86
|
+
const firmness = effectiveFirmness(
|
|
87
|
+
"req",
|
|
88
|
+
requirement.firmness,
|
|
89
|
+
requirement.origin,
|
|
90
|
+
);
|
|
91
|
+
const label =
|
|
92
|
+
firmness === "firm"
|
|
93
|
+
? "firm: ask before changing"
|
|
94
|
+
: (firmness ?? "default");
|
|
95
|
+
const statement = truncate(
|
|
96
|
+
requirement.semantic_text ?? requirement.title,
|
|
97
|
+
MAX_STATEMENT,
|
|
98
|
+
);
|
|
99
|
+
const sorted = [...entry.names].sort();
|
|
100
|
+
const names =
|
|
101
|
+
sorted.length > MAX_NAMED_SYMBOLS
|
|
102
|
+
? `${sorted.slice(0, MAX_NAMED_SYMBOLS).join(", ")} and ${sorted.length - MAX_NAMED_SYMBOLS} more`
|
|
103
|
+
: sorted.join(", ");
|
|
104
|
+
const verb = sorted.length === 1 ? "implements" : "implement";
|
|
105
|
+
diagnostics.push({
|
|
106
|
+
id: "requirement_statement_unverified",
|
|
107
|
+
severity: "warning",
|
|
108
|
+
blocking: false,
|
|
109
|
+
category: "requirement",
|
|
110
|
+
entityId: id,
|
|
111
|
+
files: [...entry.files].sort(),
|
|
112
|
+
docs: ["docs/symbol-traceability-taxonomy.md"],
|
|
113
|
+
message: `The change edits ${names}, which ${verb} ${id} [${label}]: "${statement}" Kibi compared links and facts, not the code with this statement, so no violation here does not mean the change keeps it.`,
|
|
114
|
+
suggestion:
|
|
115
|
+
firmness === "firm"
|
|
116
|
+
? `Confirm the changed code still does what ${id} says. If it no longer does, the request conflicts with a rule a person set: stop and ask whether to change ${id}; change it only with their words in origin.ref, and say in the final answer which rule changed.`
|
|
117
|
+
: `Confirm the changed code still does what ${id} says. If it no longer does, the request conflicts with a recorded rule: ask whether to change ${id} before keeping the change, then supersede or update it in the same change with the person's words, and say in the final answer which rule changed.`,
|
|
118
|
+
evidence: {
|
|
119
|
+
requirement: id,
|
|
120
|
+
firmness: firmness ?? "default",
|
|
121
|
+
statement,
|
|
122
|
+
symbols: sorted,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return diagnostics;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// implements REQ-kb-check-unverified-requirement-statements
|
|
130
|
+
/**
|
|
131
|
+
* Whether Git reports any change (staged, unstaged or untracked) to a
|
|
132
|
+
* workspace file against HEAD. Outside a repository nothing counts as changed.
|
|
133
|
+
*/
|
|
134
|
+
export function workspaceFileChanged(
|
|
135
|
+
workspaceRoot: string,
|
|
136
|
+
relativePath: string,
|
|
137
|
+
): boolean {
|
|
138
|
+
try {
|
|
139
|
+
const output = execFileSync(
|
|
140
|
+
"git",
|
|
141
|
+
["status", "--porcelain", "--", relativePath],
|
|
142
|
+
{
|
|
143
|
+
cwd: workspaceRoot,
|
|
144
|
+
encoding: "utf8",
|
|
145
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
146
|
+
},
|
|
147
|
+
);
|
|
148
|
+
return output.trim() !== "";
|
|
149
|
+
} catch {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// implements REQ-kb-check-unverified-requirement-statements
|
|
155
|
+
/**
|
|
156
|
+
* Read a requirement from its canonical document in the working tree
|
|
157
|
+
* (`.kb/requirements/<id>.md`).
|
|
158
|
+
*/
|
|
159
|
+
export function readWorkspaceRequirement(
|
|
160
|
+
workspaceRoot: string,
|
|
161
|
+
id: string,
|
|
162
|
+
): ExtractedEntity | undefined {
|
|
163
|
+
if (!/^REQ-[A-Za-z0-9._-]+$/.test(id)) return undefined;
|
|
164
|
+
try {
|
|
165
|
+
return extractFromMarkdown(
|
|
166
|
+
path.join(workspaceRoot, ".kb", "requirements", `${id}.md`),
|
|
167
|
+
).entity;
|
|
168
|
+
} catch {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
@@ -126,10 +126,31 @@ export function formatQualityDiagnosticsText(
|
|
|
126
126
|
return `${diagnostics.length} quality diagnostic${diagnostics.length === 1 ? "" : "s"} found\n${details.join("\n")}`;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Requirements the changed code implements whose statements the check did not
|
|
131
|
+
* compare with the code (`requirement_statement_unverified`).
|
|
132
|
+
*/
|
|
133
|
+
// implements REQ-kb-check-unverified-requirement-statements
|
|
134
|
+
function unverifiedRequirementIds(input: {
|
|
135
|
+
readonly impactResult: ChangedFileImpactResult | undefined;
|
|
136
|
+
}): string[] {
|
|
137
|
+
return (input.impactResult?.impactDiagnostics ?? [])
|
|
138
|
+
.filter(
|
|
139
|
+
(diagnostic) => diagnostic.id === "requirement_statement_unverified",
|
|
140
|
+
)
|
|
141
|
+
.map((diagnostic) => diagnostic.entityId)
|
|
142
|
+
.filter((id): id is string => id !== undefined);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// implements REQ-mcp-tool-check, REQ-kb-check-unverified-requirement-statements
|
|
146
|
+
export function formatViolationText(
|
|
147
|
+
violations: readonly Violation[],
|
|
148
|
+
unverifiedRequirements: readonly string[] = [],
|
|
149
|
+
): string {
|
|
131
150
|
if (violations.length === 0) {
|
|
132
|
-
return
|
|
151
|
+
return unverifiedRequirements.length === 0
|
|
152
|
+
? "No violations found"
|
|
153
|
+
: `No structural violations found. The changed code implements ${unverifiedRequirements.join(", ")}; Kibi did not compare the code with ${unverifiedRequirements.length === 1 ? "its statement" : "their statements"} (quoted under requirement_statement_unverified below), so confirm the change still does what each says, and ask the person before keeping a change that contradicts one`;
|
|
133
154
|
}
|
|
134
155
|
|
|
135
156
|
const details = violations.map((violation) => {
|
|
@@ -205,7 +226,9 @@ export function buildSummary(input: {
|
|
|
205
226
|
readonly impactResult: ChangedFileImpactResult | undefined;
|
|
206
227
|
readonly qualityDiagnostics: readonly QualityDiagnostic[];
|
|
207
228
|
}): string {
|
|
208
|
-
const sections = [
|
|
229
|
+
const sections = [
|
|
230
|
+
formatViolationText(input.violations, unverifiedRequirementIds(input)),
|
|
231
|
+
];
|
|
209
232
|
if (input.evaluated !== undefined)
|
|
210
233
|
sections.push(formatEvaluatedText(input.evaluated));
|
|
211
234
|
if (input.impactResult !== undefined) {
|
|
@@ -1359,36 +1359,43 @@ export async function executeStatus(
|
|
|
1359
1359
|
bootstrapActivation.activationState === "root_active_thin" &&
|
|
1360
1360
|
!bootstrapActivation.applyBlocked,
|
|
1361
1361
|
reason: bootstrapActivation.reason,
|
|
1362
|
+
// implements REQ-legacy-kb-first-step-migrate
|
|
1362
1363
|
nextAction:
|
|
1363
|
-
bootstrapActivation.
|
|
1364
|
+
bootstrapActivation.nextOperation !== undefined
|
|
1364
1365
|
? {
|
|
1365
|
-
operation:
|
|
1366
|
+
operation: bootstrapActivation.nextOperation,
|
|
1366
1367
|
reason: bootstrapActivation.reason,
|
|
1367
1368
|
required: true,
|
|
1368
1369
|
}
|
|
1369
|
-
: bootstrapActivation.activationState === "
|
|
1370
|
+
: bootstrapActivation.activationState === "root_uninitialized"
|
|
1370
1371
|
? {
|
|
1371
|
-
operation: "kibi
|
|
1372
|
+
operation: "kibi init",
|
|
1372
1373
|
reason: bootstrapActivation.reason,
|
|
1373
1374
|
required: true,
|
|
1374
1375
|
}
|
|
1375
|
-
: bootstrapActivation.activationState === "
|
|
1376
|
+
: bootstrapActivation.activationState === "root_partial"
|
|
1376
1377
|
? {
|
|
1377
|
-
operation: "
|
|
1378
|
+
operation: "kibi doctor",
|
|
1378
1379
|
reason: bootstrapActivation.reason,
|
|
1379
1380
|
required: true,
|
|
1380
1381
|
}
|
|
1381
|
-
: bootstrapActivation.activationState === "
|
|
1382
|
+
: bootstrapActivation.activationState === "vendored_only"
|
|
1382
1383
|
? {
|
|
1383
|
-
operation: "
|
|
1384
|
+
operation: "move-to-project-root",
|
|
1384
1385
|
reason: bootstrapActivation.reason,
|
|
1385
1386
|
required: true,
|
|
1386
1387
|
}
|
|
1387
|
-
:
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1388
|
+
: bootstrapActivation.activationState === "root_active_thin"
|
|
1389
|
+
? {
|
|
1390
|
+
operation: "kb_plan_bootstrap",
|
|
1391
|
+
reason: bootstrapActivation.reason,
|
|
1392
|
+
required: true,
|
|
1393
|
+
}
|
|
1394
|
+
: {
|
|
1395
|
+
operation: "continue-kibi-workflow",
|
|
1396
|
+
reason: bootstrapActivation.reason,
|
|
1397
|
+
required: false,
|
|
1398
|
+
},
|
|
1392
1399
|
},
|
|
1393
1400
|
};
|
|
1394
1401
|
return {
|
|
@@ -344,6 +344,16 @@ export function digestMigrationPlan(plan: MigrationPlan): MigrationPlanDigest {
|
|
|
344
344
|
});
|
|
345
345
|
else group.count += 1;
|
|
346
346
|
}
|
|
347
|
+
const automaticActions = plan.actions.filter(
|
|
348
|
+
(action) => action.safety === "automatic",
|
|
349
|
+
);
|
|
350
|
+
const blockedAutomatic = automaticActions.filter(
|
|
351
|
+
(action) => action.state === "blocked",
|
|
352
|
+
).length;
|
|
353
|
+
const blockedNote =
|
|
354
|
+
blockedAutomatic === 0
|
|
355
|
+
? ""
|
|
356
|
+
: ` ${blockedAutomatic === 1 ? "1 automatic action is" : `${blockedAutomatic} automatic actions are`} blocked: it waits for the actions in its dependsOn and needs no approval until a later plan shows it ready.`;
|
|
347
357
|
return {
|
|
348
358
|
version: MIGRATION_PLAN_DIGEST_VERSION,
|
|
349
359
|
planHash: plan.planHash,
|
|
@@ -351,9 +361,7 @@ export function digestMigrationPlan(plan: MigrationPlan): MigrationPlanDigest {
|
|
|
351
361
|
expected: plan.expected,
|
|
352
362
|
scope: plan.scope,
|
|
353
363
|
summary: plan.summary,
|
|
354
|
-
automaticActions
|
|
355
|
-
(action) => action.safety === "automatic",
|
|
356
|
-
),
|
|
364
|
+
automaticActions,
|
|
357
365
|
actionGroups: [...groups.values()].sort(
|
|
358
366
|
(left, right) =>
|
|
359
367
|
right.count - left.count ||
|
|
@@ -361,7 +369,7 @@ export function digestMigrationPlan(plan: MigrationPlan): MigrationPlanDigest {
|
|
|
361
369
|
left.state.localeCompare(right.state),
|
|
362
370
|
),
|
|
363
371
|
diagnostics: plan.diagnostics,
|
|
364
|
-
next:
|
|
372
|
+
next: `This digest cannot be applied. Run kb_status, kb_check and kb_coverage (or kibi migrate) for the full plan and its action details.${blockedNote}`,
|
|
365
373
|
};
|
|
366
374
|
}
|
|
367
375
|
|
|
@@ -166,6 +166,9 @@ function extraFailedEffects(
|
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
/** The envelope error code of a kb_upsert dry run on an invalid payload. */
|
|
170
|
+
export const UPSERT_DRY_RUN_INVALID = "UPSERT_DRY_RUN_INVALID" as const;
|
|
171
|
+
|
|
169
172
|
export function resultVersion(spec: {
|
|
170
173
|
readonly name: OperationName | string;
|
|
171
174
|
readonly resultVersion?: string;
|
|
@@ -207,15 +210,26 @@ export function toKibiResult<T>(
|
|
|
207
210
|
const migrationRefused =
|
|
208
211
|
row?.version === "kibi.migration-apply-result.v1" &&
|
|
209
212
|
row.outcome === "refused";
|
|
213
|
+
// implements REQ-kibi-symbol-link-guidance
|
|
214
|
+
// A kb_upsert dry run that finds the payload invalid reports what the
|
|
215
|
+
// write would do: refuse it. Its data still carries the errors.
|
|
216
|
+
const dryRunInvalid = row?.dryRun === true && row.valid === false;
|
|
210
217
|
let status = options.status;
|
|
211
218
|
if (!status) {
|
|
212
219
|
status = "success";
|
|
213
220
|
if (row?.status === "committed_with_repairs")
|
|
214
221
|
status = "committed_with_repairs";
|
|
215
|
-
if (row?.status === "rejected" || migrationRefused
|
|
222
|
+
if (row?.status === "rejected" || migrationRefused || dryRunInvalid)
|
|
223
|
+
status = "error";
|
|
216
224
|
}
|
|
217
|
-
const terminalError =
|
|
218
|
-
|
|
225
|
+
const terminalError = dryRunInvalid
|
|
226
|
+
? {
|
|
227
|
+
code: UPSERT_DRY_RUN_INVALID,
|
|
228
|
+
message:
|
|
229
|
+
`Dry run: kb_upsert would refuse this payload; nothing was written. ${Array.isArray(row.errors) ? row.errors.map(String).join("; ") : ""}`.trim(),
|
|
230
|
+
retryable: false,
|
|
231
|
+
}
|
|
232
|
+
: row?.status === "rejected"
|
|
219
233
|
? {
|
|
220
234
|
code: "BOOTSTRAP_PLAN_REJECTED",
|
|
221
235
|
message:
|
|
@@ -233,7 +247,7 @@ export function toKibiResult<T>(
|
|
|
233
247
|
const resultError = options.error ?? terminalError;
|
|
234
248
|
const outcome = effectOutcome(
|
|
235
249
|
status,
|
|
236
|
-
migrationRefused && options.attempted === undefined
|
|
250
|
+
(migrationRefused || dryRunInvalid) && options.attempted === undefined
|
|
237
251
|
? { ...options, attempted: false }
|
|
238
252
|
: options,
|
|
239
253
|
);
|
|
@@ -40,7 +40,7 @@ export const checkSpec = {
|
|
|
40
40
|
includeImpactDiagnostics: {
|
|
41
41
|
type: "boolean",
|
|
42
42
|
description:
|
|
43
|
-
"When true, include changed-file impact diagnostics such as symbol_granularity_violation, symbol_semantic_review_needed and
|
|
43
|
+
"When true, include changed-file impact diagnostics such as symbol_granularity_violation, symbol_semantic_review_needed, source_file_unowned and requirement_statement_unverified (each current requirement the changed code implements, with its statement and firmness, since the check never compares code with a statement) in structured output.",
|
|
44
44
|
},
|
|
45
45
|
maxDiagnostics: {
|
|
46
46
|
type: "integer",
|
|
@@ -35,7 +35,7 @@ export const compileIntentSpec = {
|
|
|
35
35
|
name: "kb_compile_intent",
|
|
36
36
|
cliName: "compile-intent",
|
|
37
37
|
description:
|
|
38
|
-
"Compile complete change intent into a deterministic, snapshot-bound read-only plan. Reuses intent-aware discovery and semantic modeling, accounts for every proposition, reports contradiction witnesses, proposes traceability links, and emits dependency-ordered kb_upsert-style steps only for resolved typed claims. No mutation side effects.",
|
|
38
|
+
"Compile complete change intent into a deterministic, snapshot-bound read-only plan. Reuses intent-aware discovery and semantic modeling, accounts for every proposition, reports contradiction witnesses, proposes traceability links, and emits dependency-ordered kb_upsert-style steps only for resolved typed claims. contradictionAnalysis.outcome is no_conflict only when the check could decide: an unmodeled proposition or a related current requirement without logic leaves it unresolved with reasons and reviewCandidates to compare and judge through conflictReviews. No mutation side effects.",
|
|
39
39
|
businessInputSchema: {
|
|
40
40
|
type: "object",
|
|
41
41
|
required: ["intent", "mode"],
|
|
@@ -198,6 +198,22 @@ export const compileIntentSpec = {
|
|
|
198
198
|
description:
|
|
199
199
|
"Explicit decisions for deterministic traceability proposals. Pending proposals never enter steps.",
|
|
200
200
|
},
|
|
201
|
+
conflictReviews: {
|
|
202
|
+
type: "array",
|
|
203
|
+
maxItems: 20,
|
|
204
|
+
items: {
|
|
205
|
+
type: "object",
|
|
206
|
+
required: ["requirementId", "verdict"],
|
|
207
|
+
additionalProperties: false,
|
|
208
|
+
properties: {
|
|
209
|
+
requirementId: { type: "string", minLength: 1 },
|
|
210
|
+
verdict: { type: "string", enum: ["conflict", "no_conflict"] },
|
|
211
|
+
note: { type: "string", maxLength: 2000 },
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
description:
|
|
215
|
+
"Your verdict on each contradictionAnalysis.reviewCandidates entry of a previous compile: whether the intent contradicts that current requirement, which Kibi could not compare logically. A conflict verdict blocks the plan; a no_conflict verdict clears that candidate. A verdict may also name a current requirement discovery did not rank.",
|
|
216
|
+
},
|
|
201
217
|
},
|
|
202
218
|
},
|
|
203
219
|
requiresProlog: true,
|
|
@@ -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.
|
|
5
|
+
version: 3.15.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -37,7 +37,7 @@ Seed branch-local Kibi knowledge from cited product intent, without creating a p
|
|
|
37
37
|
3. **Declare** `bootstrapContext`: `projectSummary`; `knowledgeSources` (id, kind, title, locator, authority, optional connector, optional `component`); `intentClaims` (statement, sourceId, reference, `excerpt` required for `intent` and `observation` claims and optional for `open_question`, optional `kind`, default `intent`, optional `component` overriding the source's, optional `rationale` with the reason the source or the human gave, never invented); `conflicts` (`claimReferences` naming two or more declared claims by sourceId and reference, plus a one-sentence `note`). Only intent claims can become requirements, and they are recorded as the people's rules: `origin.kind: import` with `sourceId:reference` as `origin.ref`, and `firmness: firm`, so a later agent asks before changing one (normative lines from repository Markdown get the same, citing `path#Lline`). Observations, open questions (tagged `review:open-question`) and conflicts (tagged `review:conflict`) become cited observation facts. Generated subject keys take the shape `component.aspect`: name the component each source or claim is about (for example `recorder`; set `component` on the knowledge source, or on a claim to override it; repository Markdown takes its file or directory name), because an intent claim whose subject is neither one word nor already dotted, with no component, is reported in the plan diagnostics and left as an authoring follow-up instead of a malformed key. The aspect is the claim's subject, or its constrained property when the subject is the component itself. An aspect over four words or 40 characters is shortened to a noun phrase (words before `when`, `while`, `for`, `and`, ...; `support_inbox.update_in_real_time_when_a_new_ticket_is_assigned...` becomes `support_inbox.update_in_real_time`) and reported as `subject-key-shortened:` with both keys; two different subjects that shorten alike get a third segment (`editor.discard_a_draft.leaving`) and a `subject-key-disambiguated:` diagnostic; claims about one subject share one subject fact, and a `subject-key-shared:` diagnostic names the key and its sources. Show these diagnostics to the human with the plan so they can rename a key before approval. Kibi never contacts these sources. It binds your declaration into the plan hash.
|
|
38
38
|
4. **Preview** with `kb_plan_bootstrap` (or `plan-bootstrap --input`), read-only. On `needs_context`, ask only its bounded questions (four at most), then preview again. When `pluginOffers` is non-empty (for example `kibi-plugin-ui` because UI component files exist), ask the human once whether to enable each offered plugin, naming its evidence files. On yes, install it as a dev dependency and add it to `package.json` `kibi.plugins` with the offered capability in `augment` mode; on no, add its name to `kibi.declinedPlugins` so it is not offered again. Either way the human decides; never enable a plugin unasked. For `kibi-plugin-ui`, step 11 also captures the UI pattern inventory (see `kibi-usage` `resources/ui-requirements.md`).
|
|
39
39
|
5. **Narrow with filters, not caps.** Read the `tldr` and the `Suppressed candidates by reason` diagnostic before individual rows. If the output is too large or mostly tooling or metadata, re-preview with `entityTypes` or declared `sourceOfTruthPaths`. Generic Markdown is off by default once you declare intent claims; set `includeGenericMarkdown: true` only for repository docs the human named. Keep confirmed intent claims: they are never capped and never use `maxCandidates`, which is the separate budget for candidates Kibi discovers (symbols, tests, repository docs). Raise it only when discovered candidates you need are `over_limit`, and never for provenance stubs: a provider entry with no claim (`Source module: …`, repository metadata, layout roots) is planned last as a `meta` fact tagged `bootstrap:provenance-stub`, `discoverySummary.provenanceStubs` counts them apart from `candidatesWithClaims`, and the `over_limit` diagnostic says how many suppressed candidates are stubs; they add no knowledge, so a plan that is mostly stubs needs `entityTypes` or declared sources, not a higher cap. Read the per-source `Knowledge source …` diagnostics: each splits the claims not planned by reason (suppressed by the activation mode, a `suppressedCandidates` row, a `sourceOnlySignals` follow-up). Report them per source and restate them before approval, or carry them to step 11 with their citation.
|
|
40
|
-
6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
|
|
40
|
+
6. **Request approval.** Show the complete `structuredContent.plan`, the full hash (never abbreviated), which candidates cite which sources, what was omitted, the open questions and declared conflicts it records for the human to decide, and any claim that contradicts code. If you find an undeclared contradiction now, add it to `conflicts` and preview again rather than resolving it. A `conflict-review:` diagnostic (and `review_against:<id>` evidence on the candidate) names current requirements Kibi could not check a planned claim against because they have no logic: compare each pair yourself and show any contradiction to the human with the plan; they decide whether to drop the claim or keep it and supersede the requirement after apply. With no answer, stop as "awaiting approval." If declined, replan and ask again with the new hash.
|
|
41
41
|
7. **Pre-apply check.** The plan you send must equal the returned plan: every top-level field, including `suppressedCandidates`, `diagnostics`, `candidates`, `actions`, and `expected`, with every array the same length and order. Never rebuild it from preview fields. If the host cannot pass the exact object, stop and say so.
|
|
42
42
|
8. **Apply once** with `kb_apply_plan`, the exact plan, and `approvedPlanHash`. The operation owns dependency ordering, source-first writes, sequential mutation, and recovery journaling. A plan with hundreds of actions can run for minutes: pass a `progressToken` (Kibi sends progress after each action) with a client that resets its timeout on progress, or pass `async: true` and poll `kb_job_status` when the host has it. If the apply is cut off, call `kb_apply_plan` with only the `recoveryJournalId` from `kb_status` or the error; it resumes an action interrupted mid-write and reclaims a lock left by a dead process. Never edit or delete `.kb/recovery` files; if recovery is refused, stop and report it.
|
|
43
43
|
9. **Read back** with `kb_query` and `kb_search` to confirm writes and citations.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
id: kibi-usage
|
|
3
3
|
name: Kibi Usage
|
|
4
4
|
description: Use Kibi's source-first, exact-Git, migration-aware, proof-aware operations safely across MCP or the trusted local CLI, including partial completion repair.
|
|
5
|
-
version: 2.
|
|
5
|
+
version: 2.20.0
|
|
6
6
|
kibiCompatibility: ">=1.0.0"
|
|
7
7
|
tags:
|
|
8
8
|
- kibi
|
|
@@ -410,6 +410,16 @@ Conditional clauses ("only when", "only if", "must not ... unless") compile
|
|
|
410
410
|
into typed `forbid` rules linked by `requires_rule`, not property facts; one
|
|
411
411
|
`kb_model` or `kb_compile_intent` cannot translate stays an `ontology_gap`.
|
|
412
412
|
|
|
413
|
+
`kb_compile_intent` reports `contradictionAnalysis.outcome: no_conflict` only
|
|
414
|
+
when it could check: Prolog compares only requirements with logic links. When
|
|
415
|
+
the intent is unmodeled or a related current requirement is prose only, the
|
|
416
|
+
outcome is `unresolved` with `reasons` and up to five `reviewCandidates`. Never
|
|
417
|
+
read `unresolved` as consistent. Compare the intent with each candidate's
|
|
418
|
+
`statement`, then compile again with `conflictReviews`
|
|
419
|
+
(`{requirementId, verdict: "conflict" | "no_conflict", note}`). On a conflict
|
|
420
|
+
the plan is `blocked`: ask the human which rule holds (a firm requirement is
|
|
421
|
+
theirs), then supersede or change it, or change the intent.
|
|
422
|
+
|
|
413
423
|
## Semantic and proof guardrails
|
|
414
424
|
|
|
415
425
|
Prolog proves only encoded facts. Keep ambiguity, ontology gaps, incomplete
|