kibi-cli 2.22.1 → 2.24.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 (159) 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-foundation.d.ts.map +1 -1
  4. package/dist/cli-register-foundation.js +1 -0
  5. package/dist/commands/check.d.ts.map +1 -1
  6. package/dist/commands/check.js +51 -3
  7. package/dist/commands/doctor.d.ts.map +1 -1
  8. package/dist/commands/doctor.js +28 -0
  9. package/dist/commands/migrate.d.ts +10 -0
  10. package/dist/commands/migrate.d.ts.map +1 -1
  11. package/dist/commands/migrate.js +81 -9
  12. package/dist/commands/sync.d.ts.map +1 -1
  13. package/dist/commands/sync.js +15 -1
  14. package/dist/decision-firmness.d.ts +16 -4
  15. package/dist/decision-firmness.d.ts.map +1 -1
  16. package/dist/decision-firmness.js +29 -6
  17. package/dist/extractors/markdown.d.ts.map +1 -1
  18. package/dist/extractors/markdown.js +39 -36
  19. package/dist/intent-search.d.ts +2 -0
  20. package/dist/intent-search.d.ts.map +1 -1
  21. package/dist/intent-search.js +43 -4
  22. package/dist/operations/bootstrap/activation.d.ts +7 -1
  23. package/dist/operations/bootstrap/activation.d.ts.map +1 -1
  24. package/dist/operations/bootstrap/activation.js +27 -2
  25. package/dist/operations/bootstrap/candidate-helpers.d.ts +6 -0
  26. package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
  27. package/dist/operations/bootstrap/candidate-helpers.js +19 -0
  28. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  29. package/dist/operations/bootstrap/generate.js +56 -16
  30. package/dist/operations/bootstrap/guidance-actions.d.ts.map +1 -1
  31. package/dist/operations/bootstrap/guidance-actions.js +14 -2
  32. package/dist/operations/bootstrap/guidance.d.ts.map +1 -1
  33. package/dist/operations/bootstrap/guidance.js +6 -2
  34. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
  35. package/dist/operations/bootstrap/intent-claims.js +2 -2
  36. package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
  37. package/dist/operations/bootstrap/markdown-candidates.js +2 -2
  38. package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
  39. package/dist/operations/bootstrap/presentation.js +75 -9
  40. package/dist/operations/bootstrap/types.d.ts +6 -0
  41. package/dist/operations/bootstrap/types.d.ts.map +1 -1
  42. package/dist/operations/migration/firm-supersede.d.ts +19 -0
  43. package/dist/operations/migration/firm-supersede.d.ts.map +1 -0
  44. package/dist/operations/migration/firm-supersede.js +60 -0
  45. package/dist/operations/migration/kb-sources.d.ts +8 -5
  46. package/dist/operations/migration/kb-sources.d.ts.map +1 -1
  47. package/dist/operations/migration/kb-sources.js +50 -14
  48. package/dist/operations/migration/schema6.d.ts.map +1 -1
  49. package/dist/operations/migration/schema6.js +52 -2
  50. package/dist/operations/migration/source-paths.d.ts +18 -1
  51. package/dist/operations/migration/source-paths.d.ts.map +1 -1
  52. package/dist/operations/migration/source-paths.js +111 -16
  53. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  54. package/dist/operations/modeling/suggest-predicates.js +22 -7
  55. package/dist/operations/mutation/requirement-semantics.d.ts +19 -1
  56. package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -1
  57. package/dist/operations/mutation/requirement-semantics.js +63 -2
  58. package/dist/operations/mutation/strict-fact.js +1 -1
  59. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  60. package/dist/operations/mutation/upsert.js +2 -1
  61. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  62. package/dist/operations/planning/apply-plan.js +22 -2
  63. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  64. package/dist/operations/semantic-advisor/analyze-prose.js +49 -17
  65. package/dist/public/impact/analyzer.d.ts.map +1 -1
  66. package/dist/public/impact/analyzer.js +7 -1
  67. package/dist/public/impact/diagnostics.d.ts.map +1 -1
  68. package/dist/public/impact/diagnostics.js +16 -3
  69. package/dist/public/impact/types.d.ts +2 -0
  70. package/dist/public/impact/types.d.ts.map +1 -1
  71. package/dist/public/operations/catalog.d.ts +8 -3
  72. package/dist/public/operations/catalog.d.ts.map +1 -1
  73. package/dist/public/operations/check-executor.d.ts.map +1 -1
  74. package/dist/public/operations/check-executor.js +10 -3
  75. package/dist/public/operations/check-helpers.d.ts +10 -0
  76. package/dist/public/operations/check-helpers.d.ts.map +1 -1
  77. package/dist/public/operations/check-helpers.js +54 -0
  78. package/dist/public/operations/contracts.d.ts.map +1 -1
  79. package/dist/public/operations/contracts.js +1 -0
  80. package/dist/public/operations/decision-firmness.d.ts +14 -0
  81. package/dist/public/operations/decision-firmness.d.ts.map +1 -1
  82. package/dist/public/operations/decision-firmness.js +60 -7
  83. package/dist/public/operations/discovery-executors.d.ts +18 -0
  84. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  85. package/dist/public/operations/discovery-executors.js +80 -8
  86. package/dist/public/operations/schema6-check-actions.d.ts +4 -0
  87. package/dist/public/operations/schema6-check-actions.d.ts.map +1 -1
  88. package/dist/public/operations/schema6-check-actions.js +39 -2
  89. package/dist/public/operations/source-path-dangling.d.ts +1 -1
  90. package/dist/public/operations/source-path-dangling.d.ts.map +1 -1
  91. package/dist/public/operations/source-path-dangling.js +11 -2
  92. package/dist/public/operations/specs/composite.d.ts +1 -1
  93. package/dist/public/operations/specs/discovery.d.ts +5 -0
  94. package/dist/public/operations/specs/discovery.d.ts.map +1 -1
  95. package/dist/public/operations/specs/discovery.js +5 -0
  96. package/dist/public/operations/specs/planning.d.ts +1 -1
  97. package/dist/public/operations/specs/planning.js +1 -1
  98. package/dist/public/operations/specs/reporting.d.ts.map +1 -1
  99. package/dist/public/operations/specs/reporting.js +6 -4
  100. package/dist/public/operations/specs/semantic.d.ts +1 -1
  101. package/dist/public/operations/specs/semantic.d.ts.map +1 -1
  102. package/dist/public/operations/specs/semantic.js +4 -2
  103. package/dist/public/operations/specs/upsert.d.ts +1 -1
  104. package/dist/public/operations/specs/upsert.js +1 -1
  105. package/dist/public/schemas/entity.js +1 -1
  106. package/dist/public/skills/kibi-bootstrap/SKILL.md +5 -4
  107. package/dist/public/skills/kibi-usage/SKILL.md +36 -16
  108. package/dist/public/skills/kibi-usage/resources/workflows.md +3 -1
  109. package/dist/schemas/entity.schema.json +1 -1
  110. package/dist/search-answer.d.ts +14 -0
  111. package/dist/search-answer.d.ts.map +1 -1
  112. package/dist/search-answer.js +51 -5
  113. package/dist/traceability/git-change-snapshot.d.ts +46 -0
  114. package/dist/traceability/git-change-snapshot.d.ts.map +1 -1
  115. package/dist/traceability/git-change-snapshot.js +187 -3
  116. package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
  117. package/dist/traceability/staged-diagnostics.js +5 -6
  118. package/dist/traceability/staged-inventory-drift.d.ts +24 -0
  119. package/dist/traceability/staged-inventory-drift.d.ts.map +1 -0
  120. package/dist/traceability/staged-inventory-drift.js +84 -0
  121. package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
  122. package/dist/traceability/staged-symbols-manifest.js +1 -2
  123. package/dist/types/entities.d.ts +2 -2
  124. package/dist/types/entities.d.ts.map +1 -1
  125. package/dist/utils/content-freshness.d.ts +9 -0
  126. package/dist/utils/content-freshness.d.ts.map +1 -1
  127. package/dist/utils/content-freshness.js +33 -0
  128. package/dist/utils/kb-manifest.d.ts +7 -0
  129. package/dist/utils/kb-manifest.d.ts.map +1 -1
  130. package/dist/utils/kb-manifest.js +6 -0
  131. package/dist/utils/merge-driver-setup.d.ts +10 -0
  132. package/dist/utils/merge-driver-setup.d.ts.map +1 -0
  133. package/dist/utils/merge-driver-setup.js +69 -0
  134. package/dist/utils/rule-registry.generated.d.ts +1 -1
  135. package/dist/utils/rule-registry.generated.js +1 -1
  136. package/dist/utils/schema-version.d.ts +1 -1
  137. package/dist/utils/schema-version.d.ts.map +1 -1
  138. package/dist/utils/schema-version.js +3 -1
  139. package/package.json +2 -2
  140. package/src/public/impact/analyzer.ts +13 -1
  141. package/src/public/impact/diagnostics.ts +20 -3
  142. package/src/public/impact/types.ts +2 -0
  143. package/src/public/operations/check-executor.ts +16 -4
  144. package/src/public/operations/check-helpers.ts +65 -0
  145. package/src/public/operations/contracts.ts +1 -0
  146. package/src/public/operations/decision-firmness.ts +83 -10
  147. package/src/public/operations/discovery-executors.ts +124 -7
  148. package/src/public/operations/schema6-check-actions.ts +43 -2
  149. package/src/public/operations/source-path-dangling.ts +12 -2
  150. package/src/public/operations/specs/discovery.ts +6 -0
  151. package/src/public/operations/specs/planning.ts +1 -1
  152. package/src/public/operations/specs/reporting.ts +6 -4
  153. package/src/public/operations/specs/semantic.ts +4 -2
  154. package/src/public/operations/specs/upsert.ts +1 -1
  155. package/src/public/schemas/entity.ts +1 -1
  156. package/src/public/skills/kibi-bootstrap/SKILL.md +5 -4
  157. package/src/public/skills/kibi-usage/SKILL.md +36 -16
  158. package/src/public/skills/kibi-usage/resources/workflows.md +3 -1
  159. package/src/schemas/entity.schema.json +1 -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.16.1
5
+ version: 2.17.0
6
6
  kibiCompatibility: ">=1.0.0"
7
7
  tags:
8
8
  - kibi
@@ -92,10 +92,12 @@ it is a pointer to the knowledge, never proof. Each line answers one question:
92
92
  - `Kibi knowledge for <path> (symbol manifest):` and the `- REQ-… — <symbol
93
93
  titles>` rows: the requirements the file's symbols implement, lead
94
94
  requirement first. Read the lead requirement with `kb_query({id})` before
95
- changing behavior.
95
+ changing behavior; add `includeBody: true` to see its authored prose, and
96
+ fix a stale sentence there with `kb_upsert` `document.body`.
96
97
  - `REQ-… must keep true: FACT-…` (edit only): the ground facts the lead
97
98
  requirement requires. An edit that breaks one of them is a requirement
98
- change: supersede and remodel the requirement, do not code around the fact.
99
+ change: ask before changing the rule (see Decision firmness), and never
100
+ code around the fact.
99
101
  - `Decision: ADR-…` (edit only): the decision behind the lead requirement.
100
102
  - `Covered by: TEST-…` and `Test code for: …`: the tests that prove the
101
103
  file's symbols; run or update them with the edit.
@@ -358,24 +360,40 @@ exists in `rationale`, or link the ADR that explains it.
358
360
 
359
361
  Requirements and ADRs carry `firmness`, which says what you may do with them.
360
362
  Search answers, `kb_query` rows and hook snippets show it (snippets label only
361
- `[firm]` and `[exploratory]`; an unlabeled decision is `default`).
363
+ `[firm: ask before changing]` and `[exploratory]`; an unlabeled decision is
364
+ `default`).
362
365
 
363
366
  - `firm`: a human stated it as a rule. Follow it; to change it, ask first.
364
- `kb_check` blocks a firm decision without `origin.ref` to their words, and
365
- a replacement of a firm decision that is not itself firm.
366
- - `default` (when absent): a delegated or agent choice. Follow it unless the
367
- task gives evidence against it; then supersede it in the same change and
368
- list it under "Decisions I changed" for review, without asking first.
367
+ A requirement or ADR with no `firmness` reads as `firm` unless an agent
368
+ wrote it (`origin.kind: agent`): rules people wrote, imported or carried
369
+ over from before origins existed are theirs. `kb_check` blocks an explicit
370
+ `firmness: firm` without `origin.ref` to their words, and a replacement of
371
+ a firm decision that is not itself firm.
372
+ - `default`: a delegated or agent choice (`firmness: default`, or an
373
+ agent-written decision without `firmness`). Follow it; supersede it only on
374
+ evidence that it is wrong (a failing test, a measured cost, a constraint it
375
+ missed), in the same change, listed under "Decisions I changed".
369
376
  - `exploratory`: a flagged guess; revisit it when you touch the area.
370
377
 
378
+ **A request that conflicts with a recorded rule is a question, whatever the
379
+ rule's firmness.** The request is not evidence against the rule: it is the
380
+ conflict a person has to decide. Name the rule (id and what it requires) and
381
+ ask whether to change it; do not implement the request and do not supersede
382
+ the rule. Go ahead only when the person has said to change that rule, in this
383
+ task or in reply: then supersede it with `firmness: firm` and `origin.ref` to
384
+ their words, and say in the final answer which rule changed.
385
+
371
386
  Set `firmness` from what a human actually said: only their stated rule is
372
387
  `firm` (with `origin.ref`); "do whatever" or your own pick is `default`; a
373
- guess is `exploratory`. A supersede needs `## Why this changed` in the new
374
- entity's body (at least 12 words): name the evidence (a failing test, a
375
- measured cost, a constraint the old decision missed, a new requirement) and
376
- answer the old decision's reasoning; `kb_check` blocks it otherwise. A
377
- preference is not evidence: propose it instead of superseding. Kibi checks
378
- only that a firm decision has a reference; never cite words nobody said.
388
+ guess is `exploratory`. Write it explicitly whenever you record a decision,
389
+ including when importing rules from documents, tests or tickets (a rule a
390
+ person wrote there is `firm`, with `origin.ref` to that document). A
391
+ supersede needs `## Why this changed` in the new entity's body (at least 12
392
+ words): name the evidence (a failing test, a measured cost, a constraint the
393
+ old decision missed, a new requirement, the person's instruction) and answer
394
+ the old decision's reasoning; `kb_check` blocks it otherwise. A preference is
395
+ not evidence: propose it instead of superseding. Kibi checks only that a firm
396
+ decision has a reference; never cite words nobody said.
379
397
 
380
398
  Conditional clauses ("only when", "only if", "must not ... unless") compile
381
399
  into typed `forbid` rules linked by `requires_rule`, not property facts; one
@@ -466,7 +484,9 @@ prose (`mode: "requirement"`, or `kb_upsert` with the extended `semantic_text`
466
484
  and the ledger `mode: "analyze"` returns) and call `mode: "predicates"` again.
467
485
  Read `decisionConflicts` and its warnings whenever `plan` is null: they name
468
486
  the stored decision facts the candidate would contradict and the requirements
469
- that own them. `provide_argument_bindings` and `record_ontology_gap` mean
487
+ that own them. To ask what a proposed rule would conflict with, call
488
+ `mode: "predicates"` with its text and no `requirementId`; nothing is written.
489
+ `provide_argument_bindings` and `record_ontology_gap` mean
470
490
  the same as for an ungrounded claim, and the gap observation is not a grounding
471
491
  link, so write it exactly as returned (tags, `claim_text`, `document.body`, no
472
492
  `claim_key`, no relationships). Record an ontology gap only when no returned schema fits the
@@ -83,7 +83,9 @@ On the first page, intent mode also returns an answer layer in `data.answer`
83
83
 
84
84
  - `governing` — current requirements matched by the query or linked to a
85
85
  matched entity, each with `via` (how it was reached) and its linked `facts`,
86
- `scenarios`, `tests`, and `adrs`.
86
+ `scenarios`, `tests`, and `adrs`. A requirement with more linked facts than
87
+ the answer lists carries `moreFacts` (`count` and the `kb_query` `ask` that
88
+ lists them all); never read a short `facts` list as the whole rule.
87
89
  - `rationale` — ADRs that explain the decisions.
88
90
  - `notGoverning` — superseded or deprecated requirements, with `supersededBy`
89
91
  when known. Never read these as current policy; superseded entities are also
@@ -521,7 +521,7 @@
521
521
  "default",
522
522
  "exploratory"
523
523
  ],
524
- "description": "Requirement and ADR only. How firmly the decision was made. firm: a human stated it as a rule; it needs a ref (origin.ref, or approval_ref on a requirement) to their words, and only a human may change it. default (assumed when absent): a delegated or agent choice; a later agent may supersede it with evidence. exploratory: a flagged guess, expected to be revisited."
524
+ "description": "Requirement and ADR only. How firmly the decision was made. firm: a human stated it as a rule; it needs a ref (origin.ref, or approval_ref on a requirement) to their words, and only a human may change it. default: a delegated or agent choice; a later agent may supersede it with evidence. exploratory: a flagged guess, expected to be revisited. When absent, it reads as default if an agent wrote the entity (origin.kind agent) and as firm otherwise (a person's, imported or migrated decision); set it explicitly when recording a decision."
525
525
  },
526
526
  "expects": {
527
527
  "type": "string",
@@ -93,6 +93,14 @@ export type SearchAnswerRequirement = SearchAnswerEntity & Readonly<{
93
93
  score: number;
94
94
  via: string;
95
95
  facts: readonly SearchAnswerFact[];
96
+ /**
97
+ * Linked facts the answer left out (over the per-requirement limit or
98
+ * cut to fit the byte budget) and the kb_query that lists them all.
99
+ */
100
+ moreFacts?: Readonly<{
101
+ count: number;
102
+ ask: string;
103
+ }>;
96
104
  scenarios: readonly SearchAnswerEntity[];
97
105
  tests: readonly SearchAnswerTest[];
98
106
  adrs: readonly SearchAnswerEntity[];
@@ -140,6 +148,12 @@ export declare const SEARCH_ANSWER_LIMITS: {
140
148
  readonly seeds: 10;
141
149
  readonly governing: 5;
142
150
  readonly perRequirement: 4;
151
+ /**
152
+ * Linked facts listed per governing requirement. Facts are what an answer
153
+ * states, so a requirement with several clauses keeps more of them than
154
+ * of its scenarios or tests.
155
+ */
156
+ readonly factsPerRequirement: 8;
143
157
  readonly rationale: 3;
144
158
  readonly notGoverning: 5;
145
159
  readonly observations: 3;
@@ -1 +1 @@
1
- {"version":3,"file":"search-answer.d.ts","sourceRoot":"","sources":["../src/search-answer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"search-answer.d.ts","sourceRoot":"","sources":["../src/search-answer.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,7 @@ const ROW_KEYS = [
43
43
  "fact_kind",
44
44
  "approved_by",
45
45
  "firmness",
46
+ "origin",
46
47
  "source",
47
48
  "sourceFile",
48
49
  "predicate_namespace",
@@ -64,6 +65,12 @@ export const SEARCH_ANSWER_LIMITS = {
64
65
  seeds: 10,
65
66
  governing: 5,
66
67
  perRequirement: 4,
68
+ /**
69
+ * Linked facts listed per governing requirement. Facts are what an answer
70
+ * states, so a requirement with several clauses keeps more of them than
71
+ * of its scenarios or tests.
72
+ */
73
+ factsPerRequirement: 8,
67
74
  rationale: 3,
68
75
  notGoverning: 5,
69
76
  observations: 3,
@@ -86,7 +93,7 @@ function unique(ids) {
86
93
  }
87
94
  function brief(entity) {
88
95
  const status = text(entity.status);
89
- const firmness = effectiveFirmness(entity.type, entity.firmness);
96
+ const firmness = effectiveFirmness(entity.type, entity.firmness, entity.origin);
90
97
  return {
91
98
  id: text(entity.id),
92
99
  title: text(entity.title),
@@ -758,11 +765,19 @@ export async function buildSearchAnswer(prolog, matches, options = {}) {
758
765
  .map((edge) => edge.from);
759
766
  const scenarioIds = unique(from("specified_by"));
760
767
  const traversed = firstOf(scenarioIds);
768
+ // A fact the question matched comes first, then the facts that ground a
769
+ // clause (requires_*) before the subject a requirement constrains: the
770
+ // subject's key is already part of each grounding fact's claim.
771
+ const linked = unique(edges
772
+ .filter((edge) => FACT_LINKS.has(edge.rel) && edge.from === req.id)
773
+ .sort((left, right) => factRank(left, seedIds) - factRank(right, seedIds))
774
+ .map((edge) => edge.to));
775
+ if (linked.length > limits.factsPerRequirement)
776
+ truncated = true;
761
777
  return {
762
778
  req,
763
- facts: firstOf(edges
764
- .filter((edge) => FACT_LINKS.has(edge.rel) && edge.from === req.id)
765
- .map((edge) => edge.to)),
779
+ facts: linked.slice(0, limits.factsPerRequirement),
780
+ factsOmitted: Math.max(0, linked.length - limits.factsPerRequirement),
766
781
  scenarios: traversed,
767
782
  directTests: unique([...from("verified_by"), ...into("validates")]),
768
783
  related: firstOf([...from("relates_to"), ...into("relates_to")]),
@@ -838,6 +853,9 @@ export async function buildSearchAnswer(prolog, matches, options = {}) {
838
853
  ...brief(fact),
839
854
  ...factDetail(fact, schemas.get(text(fact.id))),
840
855
  })),
856
+ ...(plan.factsOmitted > 0
857
+ ? { moreFacts: moreFactsOf(req.id, plan.factsOmitted) }
858
+ : {}),
841
859
  scenarios: rowsOf(plan.scenarios).map(brief),
842
860
  tests: rowsOf(testIds[index] ?? []).map((row) => ({
843
861
  ...brief(row),
@@ -892,7 +910,14 @@ function answerNote(governingCount, ungoverned) {
892
910
  const lead = top === undefined
893
911
  ? ""
894
912
  : `The top-ranked match ${top.id} (${top.type}) is linked to no requirement${governingCount === 0 ? "" : "; the governing requirements below were reached through lower-ranked matches"}. `;
913
+ // A fact no requirement links records something seen, not a decision. An
914
+ // agent comparing it with a governing requirement's fact on the same
915
+ // tuple must not weigh the two as equals.
916
+ const ungovernedFact = ungoverned.some((entry) => entry.type === "fact")
917
+ ? "A fact in ungoverned is linked to no requirement, so it decides nothing: where it disagrees with a governing requirement's fact, the governing fact holds. "
918
+ : "";
895
919
  return (lead +
920
+ ungovernedFact +
896
921
  (governingCount === 0
897
922
  ? "No current requirement matched or owns a matched entity. Absence here is not evidence that nothing governs the change; refine the query or pass sourceLocations."
898
923
  : "Governing requirements are current requirements matched by the query or linked to a matched entity. Links are discovery, not proof. A verdict lists the contradiction and scenario-feasibility witnesses that name the requirement; none means no check named it, not that it is proven. Use kb_check and kb_coverage for consistency and proof status."));
@@ -924,6 +949,17 @@ const ANSWER_TRIM_ORDER = [
924
949
  "ungoverned",
925
950
  "rationale",
926
951
  ];
952
+ /** Where a linked fact of a governing requirement sorts in its list. */
953
+ function factRank(edge, seedIds) {
954
+ const matched = seedIds.indexOf(edge.to);
955
+ if (matched >= 0)
956
+ return matched;
957
+ return seedIds.length + (edge.rel === "constrains" ? 1 : 0);
958
+ }
959
+ /** The facts an answer left out of a governing requirement, and the ask. */
960
+ function moreFactsOf(requirementId, count) {
961
+ return { count, ask: `kb_query(${JSON.stringify({ id: requirementId })})` };
962
+ }
927
963
  const REQUIREMENT_TRIM_ORDER = [
928
964
  "unknowns",
929
965
  "exceptions",
@@ -999,7 +1035,17 @@ export function fitSearchAnswer(answer, maxBytes = SEARCH_ANSWER_LIMITS.maxBytes
999
1035
  if (own) {
1000
1036
  current = {
1001
1037
  ...current,
1002
- governing: [{ ...req, [own]: req[own].slice(0, -1) }],
1038
+ governing: [
1039
+ {
1040
+ ...req,
1041
+ [own]: req[own].slice(0, -1),
1042
+ ...(own === "facts"
1043
+ ? {
1044
+ moreFacts: moreFactsOf(req.id, (req.moreFacts?.count ?? 0) + 1),
1045
+ }
1046
+ : {}),
1047
+ },
1048
+ ],
1003
1049
  };
1004
1050
  continue;
1005
1051
  }
@@ -14,7 +14,53 @@ export interface GitChangeSnapshot {
14
14
  /** Reject a changed HEAD or index tree after staged analysis. */
15
15
  assertUnchanged(): void;
16
16
  inventory: StagedPath[];
17
+ /**
18
+ * Staged merge commits only: paths whose staged content is taken unchanged
19
+ * from a merged parent. They are part of `baseTree`, so the inventory
20
+ * holds only what the merge itself wrote (its conflict resolutions and
21
+ * edits that differ from every parent).
22
+ */
23
+ mergeInherited?: readonly string[];
24
+ /**
25
+ * Staged snapshots only: knowledge files the commit moves into the
26
+ * canonical `.kb/` layout (for example after `kibi migrate`). `baseTree`
27
+ * holds each source's committed content at its target path, so the
28
+ * inventory shows only what the commit changed inside those files.
29
+ */
30
+ renameResolved?: ReadonlyArray<{
31
+ from: string;
32
+ to: string;
33
+ }>;
17
34
  }
35
+ /**
36
+ * For a staged merge commit, the tree the merge is judged against: HEAD
37
+ * with every path the merge takes unchanged from a merged parent replaced
38
+ * by that staged content. Diffing it against the staged tree leaves only
39
+ * what the merge itself wrote, so debt inherited from the merged branch is
40
+ * not charged to the merge. Null when no merge is in progress or nothing
41
+ * is inherited.
42
+ */
43
+ export declare function mergeResolutionBase(root: string, baseTree: string, headTree: string, parents: readonly string[]): {
44
+ baseTree: string;
45
+ inherited: string[];
46
+ } | null;
47
+ /**
48
+ * For a staged snapshot, the tree a commit that relocates knowledge files
49
+ * into `.kb/` is judged against: `baseTree` with each relocated file's
50
+ * committed content placed at its new path and the old path removed.
51
+ * Diffing it against the staged tree leaves only the edits the commit made
52
+ * inside those files, so findings that already existed at the old path are
53
+ * not charged to the move. Every other change, including an edit to a file
54
+ * that was already canonical, is diffed as before. Null when nothing is
55
+ * relocated.
56
+ */
57
+ export declare function renameResolutionBase(root: string, baseTree: string, headTree: string): {
58
+ baseTree: string;
59
+ renames: Array<{
60
+ from: string;
61
+ to: string;
62
+ }>;
63
+ } | null;
18
64
  /** Capture one immutable tree from the index and inventory only Git objects. */
19
65
  export declare function captureStagedSnapshot(workspaceRoot: string): GitChangeSnapshot;
20
66
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"git-change-snapshot.d.ts","sourceRoot":"","sources":["../../src/traceability/git-change-snapshot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,UAAU,EAEX,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAGpE,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gEAAgE;IAChE,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;IACzC,uEAAuE;IACvE,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChE,iEAAiE;IACjE,eAAe,IAAI,IAAI,CAAC;IACxB,SAAS,EAAE,UAAU,EAAE,CAAC;CACzB;AA4cD,gFAAgF;AAEhF,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,GACpB,iBAAiB,CAuBnB;AAED;;;;;GAKG;AAEH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,iBAAiB,CAyBnB"}
1
+ {"version":3,"file":"git-change-snapshot.d.ts","sourceRoot":"","sources":["../../src/traceability/git-change-snapshot.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAIV,UAAU,EAEX,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC;AAGpE,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gEAAgE;IAChE,OAAO,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAAC;IACzC,uEAAuE;IACvE,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChE,iEAAiE;IACjE,eAAe,IAAI,IAAI,CAAC;IACxB,SAAS,EAAE,UAAU,EAAE,CAAC;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9D;AAueD;;;;;;;GAOG;AAEH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CA6ClD;AAsED;;;;;;;;;GASG;AAEH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,GAAG,IAAI,CAsB3E;AAED,gFAAgF;AAEhF,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,GACpB,iBAAiB,CAuCnB;AAED;;;;;GAKG;AAEH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,iBAAiB,CAyBnB"}
@@ -1,5 +1,8 @@
1
1
  import { execFileSync, spawnSync } from "node:child_process";
2
- import { isEntityLanePath, isSymbolsManifestPath } from "../utils/kb-paths.js";
2
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import { ENTITY_LANES, isEntityLanePath, isKbKnowledgePath, isSymbolsManifestPath, } from "../utils/kb-paths.js";
3
6
  const GIT_EXEC_MAX_BUFFER = 64 * 1024 * 1024;
4
7
  function runGit(root, args) {
5
8
  try {
@@ -368,12 +371,188 @@ function snapshotFromTrees(root, baseTree, headTree, headCommit, readGit, assert
368
371
  inventory: buildInventory(root, baseTree, headTree),
369
372
  };
370
373
  }
374
+ /** The commits named by MERGE_HEAD while a merge commit is being made. */
375
+ function pendingMergeParents(root) {
376
+ let mergeHeadPath;
377
+ try {
378
+ mergeHeadPath = gitText(root, ["rev-parse", "--git-path", "MERGE_HEAD"]);
379
+ }
380
+ catch {
381
+ return [];
382
+ }
383
+ let text;
384
+ try {
385
+ text = readFileSync(path.resolve(root, mergeHeadPath), "utf8");
386
+ }
387
+ catch {
388
+ return [];
389
+ }
390
+ return text
391
+ .split(/\r?\n/)
392
+ .map((line) => line.trim())
393
+ .filter((line) => /^[a-f0-9]{40,64}$/.test(line));
394
+ }
395
+ function changedPaths(root, from, to) {
396
+ return runGit(root, ["diff", "--name-only", "-z", "--no-renames", from, to])
397
+ .toString("utf8")
398
+ .split("\0")
399
+ .filter(Boolean);
400
+ }
401
+ /**
402
+ * For a staged merge commit, the tree the merge is judged against: HEAD
403
+ * with every path the merge takes unchanged from a merged parent replaced
404
+ * by that staged content. Diffing it against the staged tree leaves only
405
+ * what the merge itself wrote, so debt inherited from the merged branch is
406
+ * not charged to the merge. Null when no merge is in progress or nothing
407
+ * is inherited.
408
+ */
409
+ // implements REQ-014
410
+ export function mergeResolutionBase(root, baseTree, headTree, parents) {
411
+ if (parents.length === 0)
412
+ return null;
413
+ const changed = changedPaths(root, baseTree, headTree);
414
+ if (changed.length === 0)
415
+ return null;
416
+ const inherited = new Set();
417
+ for (const parent of parents) {
418
+ const parentTree = treeForCommit(root, parent);
419
+ const differs = new Set(changedPaths(root, parentTree, headTree));
420
+ for (const file of changed) {
421
+ if (!differs.has(file))
422
+ inherited.add(file);
423
+ }
424
+ }
425
+ if (inherited.size === 0)
426
+ return null;
427
+ const paths = [...inherited].sort();
428
+ const listed = runGit(root, [
429
+ "ls-tree",
430
+ "-r",
431
+ "-z",
432
+ "--full-tree",
433
+ headTree,
434
+ "--",
435
+ ...paths.map(literalPathspec),
436
+ ])
437
+ .toString("utf8")
438
+ .split("\0")
439
+ .filter(Boolean);
440
+ const present = new Set();
441
+ const entries = [];
442
+ for (const row of listed) {
443
+ const tab = row.indexOf("\t");
444
+ if (tab < 0)
445
+ continue;
446
+ const [mode, , oid] = row.slice(0, tab).split(" ");
447
+ const file = row.slice(tab + 1);
448
+ present.add(file);
449
+ entries.push(`${mode} ${oid}\t${file}`);
450
+ }
451
+ const zero = "0".repeat(baseTree.length);
452
+ for (const file of paths) {
453
+ // Deleted on the merged side and absent from the staged tree.
454
+ if (!present.has(file))
455
+ entries.push(`0 ${zero}\t${file}`);
456
+ }
457
+ return {
458
+ baseTree: writeTreeWithEntries(root, baseTree, entries, "kibi-merge-base-"),
459
+ inherited: paths,
460
+ };
461
+ }
462
+ /** Write `baseTree` with `--index-info` entries applied in a scratch index. */
463
+ function writeTreeWithEntries(root, baseTree, entries, prefix) {
464
+ const scratch = mkdtempSync(path.join(tmpdir(), prefix));
465
+ const env = { ...process.env, GIT_INDEX_FILE: path.join(scratch, "index") };
466
+ try {
467
+ const git = (args, input) => execFileSync("git", [...args], {
468
+ cwd: root,
469
+ env,
470
+ maxBuffer: GIT_EXEC_MAX_BUFFER,
471
+ ...(input !== undefined ? { input } : {}),
472
+ stdio: ["pipe", "pipe", "ignore"],
473
+ });
474
+ git(["read-tree", baseTree]);
475
+ git(["update-index", "-z", "--index-info"], `${entries.join("\0")}\0`);
476
+ return git(["write-tree"]).toString("utf8").trim();
477
+ }
478
+ finally {
479
+ rmSync(scratch, { recursive: true, force: true });
480
+ }
481
+ }
482
+ function renamedPaths(root, from, to) {
483
+ return parseNameStatus(runGit(root, [
484
+ "diff",
485
+ "--name-status",
486
+ "-z",
487
+ "-M",
488
+ "--diff-filter=R",
489
+ from,
490
+ to,
491
+ ])).flatMap((row) => row.status === "R" && row.oldPath !== undefined
492
+ ? [{ from: row.oldPath, to: row.path }]
493
+ : []);
494
+ }
495
+ /**
496
+ * True when `from -> to` moves one knowledge file from outside the canonical
497
+ * layout into the same kind of canonical `.kb/` location: an entity lane
498
+ * file into the same lane, or a symbols manifest / coordinates file onto the
499
+ * canonical file of the same name (`.yml` and `.yaml` alike).
500
+ */
501
+ function isKnowledgeRelocation(from, to) {
502
+ if (isKbKnowledgePath(from) || !isKbKnowledgePath(to))
503
+ return false;
504
+ if (isSymbolsManifestPath(to)) {
505
+ const stem = (file) => path.posix.basename(file).replace(/\.ya?ml$/, "");
506
+ return stem(from) === stem(to);
507
+ }
508
+ const lane = to.split("/")[1] ?? "";
509
+ return (ENTITY_LANES.includes(lane) &&
510
+ `/${from}`.includes(`/${lane}/`));
511
+ }
512
+ /**
513
+ * For a staged snapshot, the tree a commit that relocates knowledge files
514
+ * into `.kb/` is judged against: `baseTree` with each relocated file's
515
+ * committed content placed at its new path and the old path removed.
516
+ * Diffing it against the staged tree leaves only the edits the commit made
517
+ * inside those files, so findings that already existed at the old path are
518
+ * not charged to the move. Every other change, including an edit to a file
519
+ * that was already canonical, is diffed as before. Null when nothing is
520
+ * relocated.
521
+ */
522
+ // implements REQ-014
523
+ export function renameResolutionBase(root, baseTree, headTree) {
524
+ const zero = "0".repeat(baseTree.length);
525
+ const renames = [];
526
+ const entries = [];
527
+ for (const rename of renamedPaths(root, baseTree, headTree)) {
528
+ if (!isKnowledgeRelocation(rename.from, rename.to))
529
+ continue;
530
+ const source = readTreeEntry(root, baseTree, rename.from);
531
+ if (source?.type !== "blob")
532
+ continue;
533
+ renames.push(rename);
534
+ entries.push(`0 ${zero}\t${rename.from}`);
535
+ entries.push(`${source.mode} ${source.oid}\t${rename.to}`);
536
+ }
537
+ if (renames.length === 0)
538
+ return null;
539
+ return {
540
+ baseTree: writeTreeWithEntries(root, baseTree, entries, "kibi-rename-base-"),
541
+ renames: renames.sort((left, right) => left.to.localeCompare(right.to)),
542
+ };
543
+ }
371
544
  /** Capture one immutable tree from the index and inventory only Git objects. */
372
545
  // implements REQ-014
373
546
  export function captureStagedSnapshot(workspaceRoot) {
374
547
  const headCommit = resolveHeadCommit(workspaceRoot);
375
- const baseTree = treeForCommit(workspaceRoot, headCommit);
376
548
  const headTree = gitText(workspaceRoot, ["write-tree"]);
549
+ const commitTree = treeForCommit(workspaceRoot, headCommit);
550
+ const merge = headCommit
551
+ ? mergeResolutionBase(workspaceRoot, commitTree, headTree, pendingMergeParents(workspaceRoot))
552
+ : null;
553
+ const mergeTree = merge?.baseTree ?? commitTree;
554
+ const relocation = renameResolutionBase(workspaceRoot, mergeTree, headTree);
555
+ const baseTree = relocation?.baseTree ?? mergeTree;
377
556
  const readGit = (args) => translateReadGit(workspaceRoot, args, baseTree, headTree, headCommit);
378
557
  const assertUnchanged = () => {
379
558
  const currentHead = resolveHeadCommit(workspaceRoot);
@@ -382,7 +561,12 @@ export function captureStagedSnapshot(workspaceRoot) {
382
561
  throw new Error("Git index or HEAD changed during staged analysis; retry the operation");
383
562
  }
384
563
  };
385
- return snapshotFromTrees(workspaceRoot, baseTree, headTree, headCommit, readGit, assertUnchanged);
564
+ const snapshot = snapshotFromTrees(workspaceRoot, baseTree, headTree, headCommit, readGit, assertUnchanged);
565
+ return {
566
+ ...snapshot,
567
+ ...(merge ? { mergeInherited: merge.inherited } : {}),
568
+ ...(relocation ? { renameResolved: relocation.renames } : {}),
569
+ };
386
570
  }
387
571
  /**
388
572
  * Capture changes between explicit revisions. The caller chooses `base`; for
@@ -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,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;AAwID,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,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"}
@@ -8,13 +8,12 @@ function deriveCoordinatesPath(symbolsManifestPath) {
8
8
  function formatFileList(paths) {
9
9
  return paths.join(", ");
10
10
  }
11
- /** Max symbol names rendered per detail line before "+N more". */
12
- const MAX_DETAIL_NAMES = 6;
11
+ /**
12
+ * Every name, in order: the gate output is the list a person works through,
13
+ * so it is never cut short.
14
+ */
13
15
  function formatNameList(names) {
14
- const shown = names.slice(0, MAX_DETAIL_NAMES);
15
- const overflow = names.length - shown.length;
16
- const rendered = shown.join(", ");
17
- return overflow > 0 ? `${rendered} … and ${overflow} more` : rendered;
16
+ return names.join(", ");
18
17
  }
19
18
  function symbolsManifestFileDetails(evidence, paths) {
20
19
  const pathSet = new Set(paths);
@@ -0,0 +1,24 @@
1
+ import type { ExtractionResult } from "../extractors/markdown.js";
2
+ /** A staged requirement whose semantic inventory no longer matches its prose. */
3
+ export declare const STAGED_INVENTORY_DRIFT_RULE = "semantic-inventory-drift";
4
+ export type StagedInventoryDriftViolation = Readonly<{
5
+ rule: typeof STAGED_INVENTORY_DRIFT_RULE;
6
+ entityId: string;
7
+ source: string;
8
+ message: string;
9
+ errors: readonly string[];
10
+ }>;
11
+ /**
12
+ * The staged requirements that declare a semantic inventory contract (now
13
+ * or in the base tree) whose inventory no longer matches their prose. The
14
+ * next `kibi sync` refuses them under the proposition-complete boundary;
15
+ * the gate applies the same test, so drift is caught at the commit that
16
+ * introduces it instead of after a later merge. Requirements without a
17
+ * contract are left to sync, which decides from its own baseline.
18
+ */
19
+ export declare function stagedInventoryDriftViolations(input: {
20
+ stagedPaths: ReadonlySet<string>;
21
+ snapshotEntities: readonly ExtractionResult[];
22
+ readBase: (path: string) => string | null;
23
+ }): StagedInventoryDriftViolation[];
24
+ //# sourceMappingURL=staged-inventory-drift.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged-inventory-drift.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-inventory-drift.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAKlE,iFAAiF;AAEjF,eAAO,MAAM,2BAA2B,6BAA6B,CAAC;AAGtE,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC;IACnD,IAAI,EAAE,OAAO,2BAA2B,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CAC3B,CAAC,CAAC;AAgCH;;;;;;;GAOG;AAEH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE;IACpD,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,gBAAgB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC3C,GAAG,6BAA6B,EAAE,CA8BlC"}