kibi-cli 2.6.0 → 2.6.1

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 (131) hide show
  1. package/dist/cli-operation-metadata.d.ts +1 -1
  2. package/dist/cli-operation-metadata.js +1 -1
  3. package/dist/commands/check.d.ts.map +1 -1
  4. package/dist/commands/check.js +15 -0
  5. package/dist/commands/init-helpers.js +1 -1
  6. package/dist/commands/migrate.d.ts.map +1 -1
  7. package/dist/commands/migrate.js +43 -0
  8. package/dist/entity-body-context.d.ts +132 -0
  9. package/dist/entity-body-context.d.ts.map +1 -0
  10. package/dist/entity-body-context.js +382 -0
  11. package/dist/extractors/markdown.d.ts +13 -0
  12. package/dist/extractors/markdown.d.ts.map +1 -1
  13. package/dist/extractors/markdown.js +19 -2
  14. package/dist/intent-search.d.ts.map +1 -1
  15. package/dist/intent-search.js +4 -4
  16. package/dist/operations/bootstrap/candidate-helpers.d.ts +18 -0
  17. package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
  18. package/dist/operations/bootstrap/candidate-helpers.js +35 -0
  19. package/dist/operations/bootstrap/candidates.d.ts.map +1 -1
  20. package/dist/operations/bootstrap/candidates.js +4 -2
  21. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
  22. package/dist/operations/bootstrap/intent-claims.js +59 -4
  23. package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
  24. package/dist/operations/bootstrap/markdown-candidates.js +7 -3
  25. package/dist/operations/bootstrap/presentation.js +1 -1
  26. package/dist/operations/migration/entity-body-context.d.ts +57 -0
  27. package/dist/operations/migration/entity-body-context.d.ts.map +1 -0
  28. package/dist/operations/migration/entity-body-context.js +261 -0
  29. package/dist/operations/migration/polarity-values.d.ts.map +1 -1
  30. package/dist/operations/migration/polarity-values.js +5 -2
  31. package/dist/operations/migration/schema6.d.ts +2 -0
  32. package/dist/operations/migration/schema6.d.ts.map +1 -1
  33. package/dist/operations/migration/schema6.js +69 -0
  34. package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
  35. package/dist/operations/modeling/logic-modeling.js +3 -1
  36. package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
  37. package/dist/operations/modeling/model-requirement.js +7 -9
  38. package/dist/operations/mutation/context-warning.d.ts +13 -0
  39. package/dist/operations/mutation/context-warning.d.ts.map +1 -0
  40. package/dist/operations/mutation/context-warning.js +59 -0
  41. package/dist/operations/mutation/origin.d.ts +2 -1
  42. package/dist/operations/mutation/origin.d.ts.map +1 -1
  43. package/dist/operations/mutation/origin.js +7 -18
  44. package/dist/operations/mutation/requirement-semantics.d.ts +16 -0
  45. package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -0
  46. package/dist/operations/mutation/requirement-semantics.js +64 -0
  47. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  48. package/dist/operations/mutation/source-authoring.js +10 -0
  49. package/dist/operations/mutation/stored-entity.d.ts +20 -0
  50. package/dist/operations/mutation/stored-entity.d.ts.map +1 -0
  51. package/dist/operations/mutation/stored-entity.js +51 -0
  52. package/dist/operations/mutation/upsert.d.ts +3 -2
  53. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  54. package/dist/operations/mutation/upsert.js +19 -10
  55. package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
  56. package/dist/operations/mutation/validate-upsert.js +6 -1
  57. package/dist/operations/planning/compile-intent.d.ts +9 -0
  58. package/dist/operations/planning/compile-intent.d.ts.map +1 -1
  59. package/dist/operations/planning/compile-intent.js +49 -2
  60. package/dist/operations/semantic-advisor/analyze-prose.d.ts +3 -2
  61. package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
  62. package/dist/operations/semantic-advisor/analyze-prose.js +3 -41
  63. package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
  64. package/dist/operations/semantic-advisor/ingestion-boundary.js +10 -1
  65. package/dist/operations/semantic-advisor/proposition-role.d.ts +6 -0
  66. package/dist/operations/semantic-advisor/proposition-role.d.ts.map +1 -0
  67. package/dist/operations/semantic-advisor/proposition-role.js +76 -0
  68. package/dist/public/operations/catalog.d.ts +33 -6
  69. package/dist/public/operations/catalog.d.ts.map +1 -1
  70. package/dist/public/operations/check-executor.d.ts.map +1 -1
  71. package/dist/public/operations/check-executor.js +3 -0
  72. package/dist/public/operations/entity-context.d.ts +51 -0
  73. package/dist/public/operations/entity-context.d.ts.map +1 -0
  74. package/dist/public/operations/entity-context.js +235 -0
  75. package/dist/public/operations/origin-review.d.ts +1 -1
  76. package/dist/public/operations/origin-review.js +2 -2
  77. package/dist/public/operations/specs/bootstrap.d.ts +14 -2
  78. package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
  79. package/dist/public/operations/specs/bootstrap.js +8 -2
  80. package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
  81. package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
  82. package/dist/public/operations/specs/check-rules.generated.js +3 -1
  83. package/dist/public/operations/specs/check.d.ts +1 -1
  84. package/dist/public/operations/specs/mutation-schemas.d.ts +1 -1
  85. package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
  86. package/dist/public/operations/specs/mutation-schemas.js +10 -2
  87. package/dist/public/operations/specs/planning.d.ts +15 -0
  88. package/dist/public/operations/specs/planning.d.ts.map +1 -1
  89. package/dist/public/operations/specs/planning.js +15 -0
  90. package/dist/public/operations/specs/upsert.d.ts +3 -3
  91. package/dist/public/operations/specs/upsert.js +2 -2
  92. package/dist/public/schemas/entity.d.ts.map +1 -1
  93. package/dist/public/schemas/entity.js +7 -1
  94. package/dist/public/skills/kibi-bootstrap/SKILL.md +3 -3
  95. package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +5 -1
  96. package/dist/public/skills/kibi-usage/SKILL.md +56 -3
  97. package/dist/public/skills/kibi-usage/resources/source-authoring.md +5 -1
  98. package/dist/schemas/entity.schema.json +14 -1
  99. package/dist/search-ranking.d.ts.map +1 -1
  100. package/dist/search-ranking.js +7 -4
  101. package/dist/traceability/staged-consistency.d.ts +25 -0
  102. package/dist/traceability/staged-consistency.d.ts.map +1 -0
  103. package/dist/traceability/staged-consistency.js +92 -0
  104. package/dist/traceability/temp-kb.d.ts.map +1 -1
  105. package/dist/traceability/temp-kb.js +5 -0
  106. package/dist/utils/kb-manifest.d.ts +5 -0
  107. package/dist/utils/kb-manifest.d.ts.map +1 -1
  108. package/dist/utils/kb-manifest.js +6 -0
  109. package/dist/utils/rule-registry.generated.d.ts +11 -0
  110. package/dist/utils/rule-registry.generated.d.ts.map +1 -1
  111. package/dist/utils/rule-registry.generated.js +13 -0
  112. package/dist/utils/schema-version.d.ts +1 -1
  113. package/dist/utils/schema-version.d.ts.map +1 -1
  114. package/dist/utils/schema-version.js +3 -2
  115. package/package.json +2 -2
  116. package/schema/manifest.json +5 -0
  117. package/schema/validation.pl +14 -2
  118. package/src/public/operations/check-executor.ts +6 -0
  119. package/src/public/operations/entity-context.ts +304 -0
  120. package/src/public/operations/origin-review.ts +2 -2
  121. package/src/public/operations/specs/bootstrap.ts +9 -2
  122. package/src/public/operations/specs/check-rules.generated.ts +3 -1
  123. package/src/public/operations/specs/mutation-schemas.ts +10 -2
  124. package/src/public/operations/specs/planning.ts +18 -0
  125. package/src/public/operations/specs/upsert.ts +2 -2
  126. package/src/public/schemas/entity.ts +7 -1
  127. package/src/public/skills/kibi-bootstrap/SKILL.md +3 -3
  128. package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +5 -1
  129. package/src/public/skills/kibi-usage/SKILL.md +56 -3
  130. package/src/public/skills/kibi-usage/resources/source-authoring.md +5 -1
  131. package/src/schemas/entity.schema.json +14 -1
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { renderRequirementBody } from "../../entity-body-context.js";
2
3
  import { buildStrictWriteSet } from "../../utils/strict-modeling.js";
3
4
  import { confidenceBand, strictPlan, upsert } from "./candidate-helpers.js";
4
5
  import { claimFor } from "./requirement-claims.js";
@@ -22,6 +23,32 @@ function digest(...parts) {
22
23
  .slice(0, 16)
23
24
  .toUpperCase();
24
25
  }
26
+ /**
27
+ * The entity body a claim persists: the statement, then a `## Source`
28
+ * section with the verbatim excerpt, the knowledge source title and the
29
+ * claim's reference. The external source (a ticket, a page) may be gone by the
30
+ * next migration; the body is what remains.
31
+ */
32
+ // implements REQ-kb-entity-body-context
33
+ function claimBody(source, claim) {
34
+ return renderRequirementBody({
35
+ statement: claim.statement,
36
+ source: {
37
+ excerpt: claim.excerpt,
38
+ title: source.title,
39
+ reference: claim.reference,
40
+ },
41
+ });
42
+ }
43
+ /**
44
+ * Attach the claim body to an upsert step. The body is the statement plus the
45
+ * cited source; no context tag is added, so a claim whose source states no
46
+ * reason is reported by entity-context-missing instead of being acknowledged.
47
+ */
48
+ // implements REQ-kb-entity-body-context
49
+ function withClaimBody(step, body) {
50
+ return { ...step, document: { body } };
51
+ }
25
52
  /**
26
53
  * An observation or open question from a declared source is evidence for the
27
54
  * human, not a requirement: it becomes a cited, non-blocking observation fact
@@ -58,7 +85,7 @@ function reviewClaimCandidate(source, claim, kind, confidence) {
58
85
  ...(claim.excerpt ? [`excerpt:${claim.excerpt}`] : []),
59
86
  ],
60
87
  relationships: [],
61
- applyPlan: [upsert(entity)],
88
+ applyPlan: [withClaimBody(upsert(entity), claimBody(source, claim))],
62
89
  };
63
90
  }
64
91
  /**
@@ -101,6 +128,17 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
101
128
  continue;
102
129
  }
103
130
  const title = `Conflict between ${refs.join(" and ")}: ${conflict.note}`;
131
+ const evidenceLines = conflict.claimReferences.map((ref) => {
132
+ const source = sources.get(ref.sourceId);
133
+ const claim = (declared.intentClaims ?? []).find((row) => row.sourceId === ref.sourceId && row.reference === ref.reference);
134
+ return [
135
+ `- ${[source?.title, ref.reference].filter(Boolean).join(" - ")}${claim ? `: ${claim.statement}` : ""}`,
136
+ ...(claim?.excerpt
137
+ ? [` > ${claim.excerpt.replace(/\s*\n\s*/g, " ")}`]
138
+ : []),
139
+ ].join("\n");
140
+ });
141
+ const conflictBody = `${conflict.note}\n\n## Evidence\n\n${evidenceLines.join("\n")}\n`;
104
142
  candidates.push({
105
143
  candidateId,
106
144
  entityType: "fact",
@@ -120,7 +158,7 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
120
158
  }),
121
159
  relationships: [],
122
160
  applyPlan: [
123
- upsert({
161
+ withClaimBody(upsert({
124
162
  type: "fact",
125
163
  id,
126
164
  title,
@@ -135,7 +173,7 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
135
173
  ...new Set(conflict.claimReferences.map((ref) => `knowledge-source:${ref.sourceId}`)),
136
174
  ],
137
175
  ],
138
- }),
176
+ }), conflictBody),
139
177
  ],
140
178
  });
141
179
  }
@@ -174,6 +212,21 @@ export function buildIntentClaimCandidates(declared, existingIds, minConfidence)
174
212
  });
175
213
  continue;
176
214
  }
215
+ // The excerpt is the only verbatim quote from the source; without it an
216
+ // intent or observation would persist as a bare sentence. Only an open
217
+ // question may omit it.
218
+ if (kind !== "open_question" && !claim.excerpt) {
219
+ suppressed.push({
220
+ candidateId: "",
221
+ reason: "missing_excerpt",
222
+ sourcePath: claim.reference,
223
+ entityType: kind === "intent" ? "req" : "fact",
224
+ statement: claim.statement,
225
+ sourceId: source.id,
226
+ });
227
+ diagnostics.push(`Claim at ${source.id}:${claim.reference} is an ${kind === "intent" ? "intent" : "observation"} claim without an excerpt; quote the passage it came from in excerpt so the entity keeps it, then re-plan.`);
228
+ continue;
229
+ }
177
230
  const confidence = AUTHORITY_CONFIDENCE[source.authority];
178
231
  if (kind !== "intent") {
179
232
  const review = reviewClaimCandidate(source, claim, kind, confidence);
@@ -222,7 +275,9 @@ export function buildIntentClaimCandidates(declared, existingIds, minConfidence)
222
275
  from,
223
276
  to,
224
277
  })),
225
- applyPlan: strictPlan(writeSet),
278
+ applyPlan: strictPlan(writeSet).map((step) => step.type === "req"
279
+ ? withClaimBody(step, claimBody(source, claim))
280
+ : step),
226
281
  });
227
282
  continue;
228
283
  }
@@ -1 +1 @@
1
- {"version":3,"file":"markdown-candidates.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/markdown-candidates.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;CACnE,CAAC;AA4HF,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,GACpB,oBAAoB,CAkFtB"}
1
+ {"version":3,"file":"markdown-candidates.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/markdown-candidates.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;CACnE,CAAC;AAuJF,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,GACpB,oBAAoB,CAkFtB"}
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { buildStrictWriteSet } from "../../utils/strict-modeling.js";
3
- import { confidenceBand, slug, strictPlan, upsert, } from "./candidate-helpers.js";
3
+ import { bootstrapProvenance, confidenceBand, provenanceBody, provenanceRequirementBody, slug, strictPlan, upsert, withDocumentBody, } from "./candidate-helpers.js";
4
4
  import { claimFor, normalizeClaimStatement } from "./requirement-claims.js";
5
5
  function headingSignals(heading, sourcePath, textRef, minConfidence) {
6
6
  const result = [];
@@ -53,7 +53,9 @@ function genericHeadingCandidate(item, heading, line, existingIds, minConfidence
53
53
  confidenceBand: confidence >= 0.95 ? "high" : confidence >= 0.8 ? "medium" : "low",
54
54
  evidence: [`generic_heading:${relativePath}#L${line}`],
55
55
  relationships: [],
56
- applyPlan: [upsert(entity)],
56
+ applyPlan: [
57
+ withDocumentBody(upsert(entity), provenanceBody(heading, bootstrapProvenance("generic markdown", `${relativePath} line ${line}`, confidence))),
58
+ ],
57
59
  };
58
60
  }
59
61
  function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence) {
@@ -93,7 +95,9 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
93
95
  from,
94
96
  to,
95
97
  })),
96
- applyPlan: strictPlan(writeSet),
98
+ applyPlan: strictPlan(writeSet).map((step) => step.type === "req"
99
+ ? withDocumentBody(step, provenanceRequirementBody(statement, bootstrapProvenance("generic markdown", `${relativePath} line ${line}${heading ? ` under the heading "${heading}"` : ""}`, confidence), `${relativePath}#L${line}`))
100
+ : step),
97
101
  };
98
102
  }
99
103
  // implements REQ-KIBI-BOOTSTRAP-PLAN
@@ -234,7 +234,7 @@ export function presentBootstrap(input) {
234
234
  const contextQuestions = [];
235
235
  if (status === "needs_context") {
236
236
  if (droppedAuthoritative.length > 0)
237
- contextQuestions.push(`No declared claim from authoritative source(s) ${droppedAuthoritative.map((row) => `"${row.source.title}"`).join(", ")} could be planned; how should those claims be restated or authored before bootstrap applies?`);
237
+ contextQuestions.push(`No declared claim from authoritative source(s) ${droppedAuthoritative.map((row) => `"${row.source.title}"`).join(", ")} could be planned; how should those claims be restated or authored before bootstrap applies? Intent and observation claims also need an excerpt (the verbatim source passage), which Kibi keeps in the entity's Source section.`);
238
238
  if (!declaredContext.projectSummary)
239
239
  contextQuestions.push("What is the one-sentence purpose of this repository?");
240
240
  if (!declaredContext.knowledgeSources)
@@ -0,0 +1,57 @@
1
+ /** Schema 8: pin the checked meaning of requirements that never stated one. */
2
+ export declare const SEMANTIC_TEXT_PIN_CODE = "semantic_text_pin";
3
+ /** Schema 8: acknowledge current entities that lack body context. */
4
+ export declare const CONTEXT_MISSING_TAG_CODE = "context_missing_tag";
5
+ /** One authored file the schema 8 migration rewrites (front matter only). */
6
+ export type BodyContextTarget = Readonly<{
7
+ id: string;
8
+ type: string;
9
+ path: string;
10
+ before: string;
11
+ after: string;
12
+ }>;
13
+ export type BodyContextPlan = Readonly<{
14
+ targets: readonly BodyContextTarget[];
15
+ /** Files Kibi cannot edit safely, with the reason. */
16
+ skipped: readonly Readonly<{
17
+ id: string;
18
+ path: string;
19
+ reason: string;
20
+ }>[];
21
+ /** Context-less entities that already carried the tag before migration. */
22
+ alreadyTagged?: readonly string[];
23
+ }>;
24
+ /**
25
+ * Ids the schema 8 migration acknowledges: every context-less entity it tags
26
+ * plus those that already carried the tag. Recorded in the manifest, which is
27
+ * the only thing that makes the tag honored by `entity-context-missing`.
28
+ */
29
+ export declare function acknowledgedContextIds(plan: BodyContextPlan): string[];
30
+ /**
31
+ * Requirements without a front-matter `semantic_text`, pinned to the value
32
+ * the pre-schema-8 derivation produces from the body (context sections
33
+ * included), so claim spans and hashes do not move when the extractor stops
34
+ * reading context sections. Read-only.
35
+ */
36
+ export declare function planSemanticTextPins(workspaceRoot: string): BodyContextPlan;
37
+ /**
38
+ * Add one tag to a document's front matter, leaving every other byte
39
+ * unchanged. Handles an absent key, a one-line flow list and a block list of
40
+ * plain items; returns null for anything else or when the edit would not read
41
+ * back as exactly that one added tag.
42
+ */
43
+ export declare function withAddedTag(content: string, tag: string): string | null;
44
+ /**
45
+ * Current requirements, scenarios, tests, ADRs and observation facts whose
46
+ * body fails the context check and that are not yet tagged, to be tagged
47
+ * `review:context-missing`. No prose is written: the tag only acknowledges the
48
+ * gap. Read-only.
49
+ */
50
+ export declare function planContextMissingTags(workspaceRoot: string): BodyContextPlan;
51
+ /** Count targets by entity type, sorted by type. */
52
+ export declare function countByType(targets: readonly Pick<BodyContextTarget, "type">[]): Record<string, number>;
53
+ /** Pin semantic_text, then return how many requirements were rewritten. */
54
+ export declare function applySemanticTextPins(workspaceRoot: string): number;
55
+ /** Tag entities that lack context, then return how many were rewritten. */
56
+ export declare function applyContextMissingTags(workspaceRoot: string): number;
57
+ //# sourceMappingURL=entity-body-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-body-context.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/entity-body-context.ts"],"names":[],"mappings":"AA0CA,+EAA+E;AAE/E,eAAO,MAAM,sBAAsB,sBAAsB,CAAC;AAC1D,qEAAqE;AAErE,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAE9D,6EAA6E;AAE7E,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CAAC;AAGH,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC;IACrC,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,sDAAsD;IACtD,OAAO,EAAE,SAAS,QAAQ,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;IAC3E,2EAA2E;IAC3E,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC,CAAC,CAAC;AAEH;;;;GAIG;AAEH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,EAAE,CAOtE;AAED;;;;;GAKG;AAEH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CA8B3E;AAMD;;;;;GAKG;AAEH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmExE;AAED;;;;;GAKG;AAEH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CAkC7E;AAED,oDAAoD;AAEpD,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,EAAE,GAClD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxB;AASD,2EAA2E;AAE3E,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,2EAA2E;AAE3E,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAKrE"}
@@ -0,0 +1,261 @@
1
+ /*
2
+ Kibi — repo-local, per-branch, queryable long-term memory for software projects
3
+ Copyright (C) 2026 Piotr Franczyk
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU Affero General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU Affero General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Affero General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ */
18
+ import { isDeepStrictEqual } from "node:util";
19
+ import { load as loadYaml } from "js-yaml";
20
+ import { CONTEXT_MISSING_TAG, assessContext, isContextAcknowledged, } from "../../entity-body-context.js";
21
+ import { legacyRequirementSemanticText } from "../../extractors/markdown.js";
22
+ import { authoredSupersededIds, contextEntityOf, isCurrentAuthoredEntity, listContextAuthoredEntities, readAuthoredEntity, } from "../../public/operations/entity-context.js";
23
+ import { readKbManifest, writeKbManifest } from "../../utils/kb-manifest.js";
24
+ import { listLaneMarkdownFiles, readText, sliceFrontmatter, withTopLevelField, writeFileAtomically, } from "./kb-sources.js";
25
+ /** Schema 8: pin the checked meaning of requirements that never stated one. */
26
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
27
+ export const SEMANTIC_TEXT_PIN_CODE = "semantic_text_pin";
28
+ /** Schema 8: acknowledge current entities that lack body context. */
29
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
30
+ export const CONTEXT_MISSING_TAG_CODE = "context_missing_tag";
31
+ /**
32
+ * Ids the schema 8 migration acknowledges: every context-less entity it tags
33
+ * plus those that already carried the tag. Recorded in the manifest, which is
34
+ * the only thing that makes the tag honored by `entity-context-missing`.
35
+ */
36
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
37
+ export function acknowledgedContextIds(plan) {
38
+ return [
39
+ ...new Set([
40
+ ...plan.targets.map((target) => target.id),
41
+ ...(plan.alreadyTagged ?? []),
42
+ ]),
43
+ ].sort();
44
+ }
45
+ /**
46
+ * Requirements without a front-matter `semantic_text`, pinned to the value
47
+ * the pre-schema-8 derivation produces from the body (context sections
48
+ * included), so claim spans and hashes do not move when the extractor stops
49
+ * reading context sections. Read-only.
50
+ */
51
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
52
+ export function planSemanticTextPins(workspaceRoot) {
53
+ const targets = [];
54
+ const skipped = [];
55
+ for (const file of listLaneMarkdownFiles(workspaceRoot, ["requirements"])) {
56
+ const before = readText(file.absolutePath);
57
+ if (before === null)
58
+ continue;
59
+ const entity = readAuthoredEntity(before, file.relativePath);
60
+ if (entity === null || entity.type !== "req")
61
+ continue;
62
+ const pinned = entity.data.semantic_text;
63
+ if (typeof pinned === "string" && pinned.trim() !== "")
64
+ continue;
65
+ const derived = legacyRequirementSemanticText(entity.body);
66
+ if (derived === "")
67
+ continue;
68
+ const after = withTopLevelField(before, "semantic_text", derived);
69
+ if (after === null) {
70
+ skipped.push({
71
+ id: entity.id,
72
+ path: file.relativePath,
73
+ reason: "semantic_text could not be edited safely",
74
+ });
75
+ continue;
76
+ }
77
+ targets.push({
78
+ id: entity.id,
79
+ type: entity.type,
80
+ path: file.relativePath,
81
+ before,
82
+ after,
83
+ });
84
+ }
85
+ return { targets, skipped };
86
+ }
87
+ function isRecord(value) {
88
+ return value !== null && typeof value === "object" && !Array.isArray(value);
89
+ }
90
+ /**
91
+ * Add one tag to a document's front matter, leaving every other byte
92
+ * unchanged. Handles an absent key, a one-line flow list and a block list of
93
+ * plain items; returns null for anything else or when the edit would not read
94
+ * back as exactly that one added tag.
95
+ */
96
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
97
+ export function withAddedTag(content, tag) {
98
+ const slice = sliceFrontmatter(content);
99
+ if (slice === null)
100
+ return null;
101
+ let before;
102
+ try {
103
+ before = loadYaml(slice.text);
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ if (!isRecord(before))
109
+ return null;
110
+ const existing = before.tags;
111
+ if (existing !== undefined && existing !== null && !Array.isArray(existing))
112
+ return null;
113
+ const eol = slice.eol;
114
+ const lines = slice.text.split(/\r?\n/);
115
+ // The text ends with a line break, so the last element is empty.
116
+ const body = lines.slice(0, -1);
117
+ const keyIndex = body.findIndex((line) => /^tags[ \t]*:/.test(line));
118
+ let next;
119
+ if (keyIndex < 0) {
120
+ next = [...body, "tags:", ` - ${tag}`];
121
+ }
122
+ else {
123
+ const line = body[keyIndex] ?? "";
124
+ const rest = line.slice(line.indexOf(":") + 1).trim();
125
+ if (rest.startsWith("[") && rest.endsWith("]")) {
126
+ const inner = rest.slice(1, -1).trim();
127
+ const edited = `${line.slice(0, line.indexOf(":") + 1)} [${inner === "" ? tag : `${inner}, ${tag}`}]`;
128
+ next = [...body.slice(0, keyIndex), edited, ...body.slice(keyIndex + 1)];
129
+ }
130
+ else if (rest === "" || rest === "null" || rest === "~") {
131
+ let end = keyIndex + 1;
132
+ let indent = " ";
133
+ while (end < body.length) {
134
+ const item = /^([ \t]*)-[ \t]+\S/.exec(body[end] ?? "");
135
+ if (item === null)
136
+ break;
137
+ if (end === keyIndex + 1)
138
+ indent = item[1] ?? "";
139
+ end += 1;
140
+ }
141
+ const head = rest === "" ? line : `${line.slice(0, line.indexOf(":") + 1)}`;
142
+ next = [
143
+ ...body.slice(0, keyIndex),
144
+ head,
145
+ ...body.slice(keyIndex + 1, end),
146
+ `${indent}- ${tag}`,
147
+ ...body.slice(end),
148
+ ];
149
+ }
150
+ else {
151
+ return null;
152
+ }
153
+ }
154
+ const text = `${next.join(eol)}${eol}`;
155
+ try {
156
+ const after = loadYaml(text);
157
+ if (!isRecord(after))
158
+ return null;
159
+ const { tags: beforeTags, ...beforeRest } = before;
160
+ const { tags: afterTags, ...afterRest } = after;
161
+ const expected = [...(Array.isArray(beforeTags) ? beforeTags : []), tag];
162
+ if (!isDeepStrictEqual(afterTags, expected) ||
163
+ !isDeepStrictEqual(beforeRest, afterRest)) {
164
+ return null;
165
+ }
166
+ }
167
+ catch {
168
+ return null;
169
+ }
170
+ return `${slice.prefix}${text}${slice.suffix}`;
171
+ }
172
+ /**
173
+ * Current requirements, scenarios, tests, ADRs and observation facts whose
174
+ * body fails the context check and that are not yet tagged, to be tagged
175
+ * `review:context-missing`. No prose is written: the tag only acknowledges the
176
+ * gap. Read-only.
177
+ */
178
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
179
+ export function planContextMissingTags(workspaceRoot) {
180
+ const entities = listContextAuthoredEntities(workspaceRoot);
181
+ const superseded = authoredSupersededIds(workspaceRoot, entities);
182
+ const targets = [];
183
+ const skipped = [];
184
+ const alreadyTagged = [];
185
+ for (const entity of entities) {
186
+ if (!isCurrentAuthoredEntity(entity, superseded))
187
+ continue;
188
+ const view = contextEntityOf(entity);
189
+ if (assessContext(entity.type, entity.body, view).ok)
190
+ continue;
191
+ if (isContextAcknowledged(view.tags)) {
192
+ alreadyTagged.push(entity.id);
193
+ continue;
194
+ }
195
+ const before = readText(`${workspaceRoot}/${entity.path}`);
196
+ if (before === null)
197
+ continue;
198
+ const after = withAddedTag(before, CONTEXT_MISSING_TAG);
199
+ if (after === null) {
200
+ skipped.push({
201
+ id: entity.id,
202
+ path: entity.path,
203
+ reason: "tags could not be edited safely",
204
+ });
205
+ continue;
206
+ }
207
+ targets.push({
208
+ id: entity.id,
209
+ type: entity.type,
210
+ path: entity.path,
211
+ before,
212
+ after,
213
+ });
214
+ }
215
+ return { targets, skipped, alreadyTagged };
216
+ }
217
+ /** Count targets by entity type, sorted by type. */
218
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
219
+ export function countByType(targets) {
220
+ const counts = {};
221
+ for (const target of targets) {
222
+ counts[target.type] = (counts[target.type] ?? 0) + 1;
223
+ }
224
+ return Object.fromEntries(Object.entries(counts).sort(([left], [right]) => left.localeCompare(right)));
225
+ }
226
+ function applyPlan(workspaceRoot, plan) {
227
+ for (const target of plan.targets) {
228
+ writeFileAtomically(`${workspaceRoot}/${target.path}`, target.after);
229
+ }
230
+ return plan.targets.length;
231
+ }
232
+ /** Pin semantic_text, then return how many requirements were rewritten. */
233
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
234
+ export function applySemanticTextPins(workspaceRoot) {
235
+ return applyPlan(workspaceRoot, planSemanticTextPins(workspaceRoot));
236
+ }
237
+ /** Tag entities that lack context, then return how many were rewritten. */
238
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
239
+ export function applyContextMissingTags(workspaceRoot) {
240
+ const plan = planContextMissingTags(workspaceRoot);
241
+ const count = applyPlan(workspaceRoot, plan);
242
+ recordContextAcknowledged(workspaceRoot, acknowledgedContextIds(plan));
243
+ return count;
244
+ }
245
+ /**
246
+ * Record the acknowledged ids in the manifest. The tag alone is never honored
247
+ * by `entity-context-missing`; the manifest is what the migration vouches with.
248
+ */
249
+ function recordContextAcknowledged(workspaceRoot, ids) {
250
+ if (ids.length === 0)
251
+ return;
252
+ const manifest = readKbManifest(workspaceRoot);
253
+ if (manifest === null)
254
+ return;
255
+ writeKbManifest(workspaceRoot, {
256
+ ...manifest,
257
+ contextAcknowledged: [
258
+ ...new Set([...(manifest.contextAcknowledged ?? []), ...ids]),
259
+ ].sort(),
260
+ });
261
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"polarity-values.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/polarity-values.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AAEtE,6EAA6E;AAE7E,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,GACpB,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CA+DxE;AAGD,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAKxE"}
1
+ {"version":3,"file":"polarity-values.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/polarity-values.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,4BAA4B,4BAA4B,CAAC;AAEtE,6EAA6E;AAE7E,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,GACpB,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,CAiExE;AAGD,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAKxE"}
@@ -35,7 +35,10 @@ export function planPolarityValueBackfill(workspaceRoot) {
35
35
  typeof data.property_key !== "string")
36
36
  throw new Error(`Cannot migrate polarity fact ${file.relativePath}: subject_key and property_key are required`);
37
37
  const after = `${slice.prefix}${slice.text}operator: eq${slice.eol}value_type: bool${slice.eol}value_bool: true${slice.eol}${slice.suffix}`;
38
- const parsed = loadYaml(sliceFrontmatter(after).text);
38
+ const afterFrontmatter = sliceFrontmatter(after);
39
+ if (!afterFrontmatter)
40
+ throw new Error(`Polarity migration could not verify ${file.relativePath}`);
41
+ const parsed = loadYaml(afterFrontmatter.text);
39
42
  if (parsed.operator !== "eq" ||
40
43
  parsed.value_type !== "bool" ||
41
44
  parsed.value_bool !== true)
@@ -43,7 +46,7 @@ export function planPolarityValueBackfill(workspaceRoot) {
43
46
  targets.push({
44
47
  id: typeof data.id === "string"
45
48
  ? data.id
46
- : file.relativePath.split("/").at(-1).slice(0, -3),
49
+ : (file.relativePath.split("/").at(-1) ?? "").slice(0, -3),
47
50
  path: file.relativePath,
48
51
  before,
49
52
  after,
@@ -2,6 +2,8 @@ import { type MigrationAction } from "../../public/operations/migration-plan.js"
2
2
  import { CLOSE_SUPERSEDED_REQUIREMENTS_CODE, MIGRATION_SYNC_ACTION_ID, MIGRATION_SYNC_CODE, SOURCE_PATH_REWRITE_CODE } from "../../public/operations/schema6-check-actions.js";
3
3
  /** The KB schema generation that introduced entity `origin`. */
4
4
  export declare const ORIGIN_SCHEMA_VERSION = 6;
5
+ /** The KB schema generation that requires body context on current entities. */
6
+ export declare const BODY_CONTEXT_SCHEMA_VERSION = 8;
5
7
  /** Automatic: re-derive a drifted inventory with the current advisor. */
6
8
  export declare const SEMANTIC_INVENTORY_REDERIVE_CODE = "semantic_inventory_rederive";
7
9
  /** Review: a drifted inventory Kibi cannot re-derive safely. */
@@ -1 +1 @@
1
- {"version":3,"file":"schema6.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/schema6.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAEL,kCAAkC,EAClC,wBAAwB,EACxB,mBAAmB,EAEnB,wBAAwB,EAOzB,MAAM,kDAAkD,CAAC;AA2B1D,gEAAgE;AAEhE,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,yEAAyE;AAEzE,eAAO,MAAM,gCAAgC,gCAAgC,CAAC;AAC9E,gEAAgE;AAEhE,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAC1E,yEAAyE;AAEzE,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE,eAAO,MAAM,gCAAgC,2BAA2B,CAAC;AACzE,OAAO,EACL,kCAAkC,EAClC,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,GACzB,CAAC;AAsEF;;;GAGG;AAEH,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,MAAM,GACpB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsDnE;AAGD,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,uDAAuD;IACvD,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC;AAEH;;;;;;GAMG;AAEH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,GAAG,wBAAwB,CA8F3B;AAED;;;;;;GAMG;AAEH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG;IAClE,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAoCA;AAED,qEAAqE;AAErE,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,eAAe,CAEjB;AA0CD,KAAK,YAAY,GAAG,QAAQ,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC,CAAC;AAEH,4DAA4D;AAE5D,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CA8Df;AAGD,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAOtD,CAAC"}
1
+ {"version":3,"file":"schema6.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/schema6.ts"],"names":[],"mappings":"AAuBA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAEL,kCAAkC,EAClC,wBAAwB,EACxB,mBAAmB,EAEnB,wBAAwB,EAOzB,MAAM,kDAAkD,CAAC;AAoC1D,gEAAgE;AAEhE,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,+EAA+E;AAE/E,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,yEAAyE;AAEzE,eAAO,MAAM,gCAAgC,gCAAgC,CAAC;AAC9E,gEAAgE;AAEhE,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAC1E,yEAAyE;AAEzE,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AAEpE,eAAO,MAAM,gCAAgC,2BAA2B,CAAC;AACzE,OAAO,EACL,kCAAkC,EAClC,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,GACzB,CAAC;AAsEF;;;GAGG;AAEH,wBAAgB,+BAA+B,CAC7C,aAAa,EAAE,MAAM,GACpB,KAAK,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAsDnE;AAGD,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,uDAAuD;IACvD,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC,CAAC,CAAC;AAEH;;;;;;GAMG;AAEH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC,GAAG,wBAAwB,CA6J3B;AAED;;;;;;GAMG;AAEH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG;IAClE,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAoCA;AAED,qEAAqE;AAErE,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,eAAe,CAEjB;AA0CD,KAAK,YAAY,GAAG,QAAQ,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,IAAI,CAAC;CACnB,CAAC,CAAC;AAEH,4DAA4D;AAE5D,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAsEf;AAGD,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAStD,CAAC"}
@@ -20,6 +20,7 @@ import { extractFromMarkdown, } from "../../extractors/markdown.js";
20
20
  import { migrationAction, } from "../../public/operations/migration-plan.js";
21
21
  import { CLOSE_SUPERSEDED_REQUIREMENTS_ACTION_ID, CLOSE_SUPERSEDED_REQUIREMENTS_CODE, MIGRATION_SYNC_ACTION_ID, MIGRATION_SYNC_CODE, SOURCE_PATH_REWRITE_ACTION_ID, SOURCE_PATH_REWRITE_CODE, closeSupersededRequirementsActionInput, exceptionUnapprovedActionInput, migrationSyncActionInput, sourcePathReviewActionInput, sourcePathRewriteActionInput, supersessionCycleReviewActionInput, } from "../../public/operations/schema6-check-actions.js";
22
22
  import { readAllShards } from "../../relationships/shards.js";
23
+ import { CONTEXT_MISSING_TAG_CODE, SEMANTIC_TEXT_PIN_CODE, applyContextMissingTags, applySemanticTextPins, countByType, planContextMissingTags, planSemanticTextPins, } from "./entity-body-context.js";
23
24
  import { applyInventoryRederivation, planInventoryRederivation, } from "./inventory-rederive.js";
24
25
  import { listLaneMarkdownFiles } from "./kb-sources.js";
25
26
  import { applyOriginBackfill, planOriginBackfill } from "./origin-backfill.js";
@@ -29,6 +30,9 @@ import { applySupersededClosures, planSupersededClosures, } from "./superseded-c
29
30
  /** The KB schema generation that introduced entity `origin`. */
30
31
  // implements REQ-kibi-schema6-migration, REQ-cli-schema-migration
31
32
  export const ORIGIN_SCHEMA_VERSION = 6;
33
+ /** The KB schema generation that requires body context on current entities. */
34
+ // implements REQ-kb-entity-body-context, REQ-cli-schema-migration
35
+ export const BODY_CONTEXT_SCHEMA_VERSION = 8;
32
36
  /** Automatic: re-derive a drifted inventory with the current advisor. */
33
37
  // implements REQ-kibi-schema6-migration, REQ-cli-schema-migration
34
38
  export const SEMANTIC_INVENTORY_REDERIVE_CODE = "semantic_inventory_rederive";
@@ -190,6 +194,61 @@ export function buildSchema6MigrationFragment(input) {
190
194
  },
191
195
  }));
192
196
  }
197
+ // Schema 8: pin the checked meaning, then acknowledge missing context. A
198
+ // missing or invalid version upgrades through every step, so it is planned.
199
+ if (input.currentSchemaVersion === null ||
200
+ input.currentSchemaVersion < BODY_CONTEXT_SCHEMA_VERSION) {
201
+ const pins = planSemanticTextPins(input.workspaceRoot);
202
+ if (pins.targets.length > 0) {
203
+ const id = "semantic-text-pin";
204
+ sourceRewriteActionIds.push(id);
205
+ actions.push(migrationAction({
206
+ id,
207
+ code: SEMANTIC_TEXT_PIN_CODE,
208
+ category: "schema",
209
+ safety: "automatic",
210
+ autoApplicable: true,
211
+ invocation: {
212
+ kind: "cli",
213
+ command_argv: ["kibi", "migrate", "--yes"],
214
+ },
215
+ affectedEntityIds: pins.targets.map((row) => row.id),
216
+ affectedFiles: pins.targets.map((row) => row.path),
217
+ postconditions: [{ requirementsWithoutSemanticText: 0 }],
218
+ evidence: {
219
+ reason: "Schema 8 stops reading context sections (Context, Rationale, Why, Background, Source, Notes, Evidence) as part of a requirement's meaning. Requirements that never stated semantic_text get the value the old derivation produced, so claim spans and hashes do not move.",
220
+ count: pins.targets.length,
221
+ preserves: "every other front matter key and the body bytes",
222
+ },
223
+ }));
224
+ }
225
+ const tags = planContextMissingTags(input.workspaceRoot);
226
+ if (tags.targets.length > 0) {
227
+ const id = "context-missing-tag";
228
+ sourceRewriteActionIds.push(id);
229
+ actions.push(migrationAction({
230
+ id,
231
+ code: CONTEXT_MISSING_TAG_CODE,
232
+ category: "schema",
233
+ safety: "automatic",
234
+ autoApplicable: true,
235
+ invocation: {
236
+ kind: "cli",
237
+ command_argv: ["kibi", "migrate", "--yes"],
238
+ },
239
+ dependsOn: pins.targets.length > 0 ? ["semantic-text-pin"] : [],
240
+ affectedEntityIds: tags.targets.map((row) => row.id),
241
+ affectedFiles: tags.targets.map((row) => row.path),
242
+ postconditions: [{ rule: "entity-context-missing", findings: 0 }],
243
+ evidence: {
244
+ reason: "Schema 8 blocks current requirements, scenarios, tests, ADRs and observation facts whose body states no context. Existing ones are tagged review:context-missing (acknowledged legacy, counted by entity-context-acknowledged); no prose is written and no reason is invented.",
245
+ count: tags.targets.length,
246
+ byType: countByType(tags.targets),
247
+ preserves: "every other front matter key and the body bytes",
248
+ },
249
+ }));
250
+ }
251
+ }
193
252
  const drift = planInventoryRederivation(input.workspaceRoot);
194
253
  for (const plan of drift) {
195
254
  const action = plan.safe ? rederiveAction(plan) : reviewAction(plan);
@@ -330,6 +389,14 @@ export async function applySchema6MigrationAction(action, context) {
330
389
  applyPolarityValueBackfill(context.workspaceRoot);
331
390
  return;
332
391
  }
392
+ case SEMANTIC_TEXT_PIN_CODE: {
393
+ applySemanticTextPins(context.workspaceRoot);
394
+ return;
395
+ }
396
+ case CONTEXT_MISSING_TAG_CODE: {
397
+ applyContextMissingTags(context.workspaceRoot);
398
+ return;
399
+ }
333
400
  case SEMANTIC_INVENTORY_REDERIVE_CODE: {
334
401
  const requirementId = action.affectedEntityIds[0];
335
402
  if (requirementId === undefined)
@@ -371,6 +438,8 @@ export async function applySchema6MigrationAction(action, context) {
371
438
  // implements REQ-kibi-schema6-migration, REQ-cli-schema-migration
372
439
  export const SCHEMA6_AUTOMATIC_CODES = new Set([
373
440
  POLARITY_VALUE_BACKFILL_CODE,
441
+ SEMANTIC_TEXT_PIN_CODE,
442
+ CONTEXT_MISSING_TAG_CODE,
374
443
  SEMANTIC_INVENTORY_REDERIVE_CODE,
375
444
  ENTITY_ORIGIN_BACKFILL_CODE,
376
445
  CLOSE_SUPERSEDED_REQUIREMENTS_CODE,
@@ -1 +1 @@
1
- {"version":3,"file":"logic-modeling.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/logic-modeling.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,mBAAmB,CAAC;AAQ3B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAUD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,mBAAmB,GACzB,oBAAoB,CAkHtB"}
1
+ {"version":3,"file":"logic-modeling.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/logic-modeling.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,WAAW,EAIjB,MAAM,mBAAmB,CAAC;AAS3B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAUD,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,mBAAmB,GACzB,oBAAoB,CAoHtB"}