kibi-cli 2.25.0 → 2.26.2

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 (102) hide show
  1. package/dist/cli-errors.d.ts +14 -0
  2. package/dist/cli-errors.d.ts.map +1 -1
  3. package/dist/cli-errors.js +31 -0
  4. package/dist/commands/check.d.ts.map +1 -1
  5. package/dist/commands/check.js +28 -0
  6. package/dist/commands/migrate.d.ts +2 -0
  7. package/dist/commands/migrate.d.ts.map +1 -1
  8. package/dist/commands/migrate.js +125 -6
  9. package/dist/commands/sync/cache.d.ts +30 -0
  10. package/dist/commands/sync/cache.d.ts.map +1 -1
  11. package/dist/commands/sync/cache.js +61 -0
  12. package/dist/commands/sync/extraction.d.ts +6 -0
  13. package/dist/commands/sync/extraction.d.ts.map +1 -1
  14. package/dist/commands/sync/extraction.js +4 -1
  15. package/dist/commands/sync.d.ts.map +1 -1
  16. package/dist/commands/sync.js +27 -1
  17. package/dist/intent-search.d.ts.map +1 -1
  18. package/dist/intent-search.js +14 -3
  19. package/dist/operations/bootstrap/claim-restatement.d.ts +37 -0
  20. package/dist/operations/bootstrap/claim-restatement.d.ts.map +1 -0
  21. package/dist/operations/bootstrap/claim-restatement.js +83 -0
  22. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  23. package/dist/operations/bootstrap/generate.js +23 -12
  24. package/dist/operations/bootstrap/intent-claims.d.ts +5 -2
  25. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
  26. package/dist/operations/bootstrap/intent-claims.js +44 -4
  27. package/dist/operations/migration/requirement-status.d.ts +41 -2
  28. package/dist/operations/migration/requirement-status.d.ts.map +1 -1
  29. package/dist/operations/migration/requirement-status.js +75 -7
  30. package/dist/operations/migration/schema6.d.ts +9 -5
  31. package/dist/operations/migration/schema6.d.ts.map +1 -1
  32. package/dist/operations/migration/schema6.js +82 -13
  33. package/dist/operations/migration/symbol-coordinates-refresh.d.ts +36 -0
  34. package/dist/operations/migration/symbol-coordinates-refresh.d.ts.map +1 -0
  35. package/dist/operations/migration/symbol-coordinates-refresh.js +99 -0
  36. package/dist/operations/mutation/source-authoring.d.ts +9 -0
  37. package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
  38. package/dist/operations/mutation/source-authoring.js +50 -2
  39. package/dist/operations/mutation/stored-entity.d.ts +5 -0
  40. package/dist/operations/mutation/stored-entity.d.ts.map +1 -1
  41. package/dist/operations/mutation/types.d.ts +1 -1
  42. package/dist/operations/mutation/types.d.ts.map +1 -1
  43. package/dist/operations/mutation/uncompiled-source.d.ts +43 -0
  44. package/dist/operations/mutation/uncompiled-source.d.ts.map +1 -0
  45. package/dist/operations/mutation/uncompiled-source.js +115 -0
  46. package/dist/operations/mutation/upsert.d.ts +6 -0
  47. package/dist/operations/mutation/upsert.d.ts.map +1 -1
  48. package/dist/operations/mutation/upsert.js +32 -5
  49. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  50. package/dist/operations/planning/apply-plan.js +4 -2
  51. package/dist/public/impact/analyzer.d.ts.map +1 -1
  52. package/dist/public/impact/analyzer.js +13 -3
  53. package/dist/public/impact/diagnostics.js +1 -1
  54. package/dist/public/impact/unowned-files.d.ts +31 -0
  55. package/dist/public/impact/unowned-files.d.ts.map +1 -0
  56. package/dist/public/impact/unowned-files.js +171 -0
  57. package/dist/public/operations/catalog.d.ts +2 -2
  58. package/dist/public/operations/discovery-entities.d.ts +11 -0
  59. package/dist/public/operations/discovery-entities.d.ts.map +1 -1
  60. package/dist/public/operations/discovery-entities.js +28 -0
  61. package/dist/public/operations/discovery-executors.d.ts.map +1 -1
  62. package/dist/public/operations/discovery-executors.js +13 -1
  63. package/dist/public/operations/migration-plan.d.ts +25 -0
  64. package/dist/public/operations/migration-plan.d.ts.map +1 -1
  65. package/dist/public/operations/migration-plan.js +40 -0
  66. package/dist/public/operations/result-envelope.d.ts +2 -0
  67. package/dist/public/operations/result-envelope.d.ts.map +1 -1
  68. package/dist/public/operations/result-envelope.js +1 -0
  69. package/dist/public/operations/source-relationship-parity.d.ts.map +1 -1
  70. package/dist/public/operations/source-relationship-parity.js +22 -7
  71. package/dist/public/operations/specs/check.d.ts +1 -1
  72. package/dist/public/operations/specs/check.js +1 -1
  73. package/dist/public/operations/specs/upsert.d.ts +1 -1
  74. package/dist/public/operations/specs/upsert.js +1 -1
  75. package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
  76. package/dist/public/skills/kibi-usage/SKILL.md +11 -5
  77. package/dist/search-answer.d.ts +9 -0
  78. package/dist/search-answer.d.ts.map +1 -1
  79. package/dist/search-answer.js +66 -3
  80. package/dist/traceability/git-change-snapshot.d.ts.map +1 -1
  81. package/dist/traceability/git-change-snapshot.js +44 -3
  82. package/dist/traceability/staged-diagnostics.d.ts +1 -1
  83. package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
  84. package/dist/traceability/staged-migration.d.ts +13 -0
  85. package/dist/traceability/staged-migration.d.ts.map +1 -0
  86. package/dist/traceability/staged-migration.js +59 -0
  87. package/dist/traceability/staged-symbols-manifest.d.ts +11 -0
  88. package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
  89. package/dist/traceability/staged-symbols-manifest.js +50 -16
  90. package/package.json +2 -2
  91. package/src/public/impact/analyzer.ts +21 -7
  92. package/src/public/impact/diagnostics.ts +1 -1
  93. package/src/public/impact/unowned-files.ts +229 -0
  94. package/src/public/operations/discovery-entities.ts +36 -0
  95. package/src/public/operations/discovery-executors.ts +17 -1
  96. package/src/public/operations/migration-plan.ts +72 -0
  97. package/src/public/operations/result-envelope.ts +3 -0
  98. package/src/public/operations/source-relationship-parity.ts +22 -7
  99. package/src/public/operations/specs/check.ts +1 -1
  100. package/src/public/operations/specs/upsert.ts +1 -1
  101. package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
  102. package/src/public/skills/kibi-usage/SKILL.md +11 -5
@@ -16,6 +16,8 @@
16
16
  along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
18
  import path from "node:path";
19
+ import { extractFromMarkdown } from "../../extractors/markdown.js";
20
+ import { withAddedTag } from "./entity-body-context.js";
19
21
  import { planInventoryRederivation, } from "./inventory-rederive.js";
20
22
  import { listLaneMarkdownFiles, readText, readTopLevelField, sliceFrontmatter, withTopLevelField, writeFileAtomically, } from "./kb-sources.js";
21
23
  /**
@@ -25,8 +27,12 @@ import { listLaneMarkdownFiles, readText, readTopLevelField, sliceFrontmatter, w
25
27
  * it is not a current requirement: the proof ladder skips it and the
26
28
  * ingestion boundary does not validate its semantic inventory, so inventory
27
29
  * drift stays hidden until someone maps the status. Which current status a
28
- * legacy one means is a semantic choice, so the mapping is only suggested;
29
- * the latent drift is computed as if the suggestion were applied.
30
+ * legacy one means is usually a semantic choice, so the mapping is only
31
+ * suggested; the latent drift is computed as if the suggestion were applied.
32
+ * Delivery vocabulary (`implemented`, `done`, `completed`) has one lossless
33
+ * reading, `closed` plus the tag `implemented`, and is mapped automatically:
34
+ * those statuses are outside the entity schema, so `kibi sync` cannot
35
+ * compile such a requirement at all until it is mapped.
30
36
  */
31
37
  /** Statuses `kibi check` accepts on a requirement (canonical and legacy). */
32
38
  const REQUIREMENT_STATUSES = new Set([
@@ -50,6 +56,29 @@ export const SUGGESTED_REQUIREMENT_STATUS = {
50
56
  superseded: "closed",
51
57
  rejected: "closed",
52
58
  };
59
+ /** The tag that keeps a delivered legacy requirement's meaning once closed. */
60
+ // implements REQ-cli-schema-migration
61
+ export const DELIVERED_REQUIREMENT_TAG = "implemented";
62
+ /**
63
+ * Legacy delivery statuses and their one lossless current reading: `closed`
64
+ * with the tag `implemented`, which kb.pl reads as a delivered requirement
65
+ * that still owes coverage. `kibi migrate` applies these without review.
66
+ */
67
+ // implements REQ-cli-schema-migration
68
+ export const DELIVERED_REQUIREMENT_STATUS = {
69
+ implemented: { status: "closed", tag: DELIVERED_REQUIREMENT_TAG },
70
+ done: { status: "closed", tag: DELIVERED_REQUIREMENT_TAG },
71
+ completed: { status: "closed", tag: DELIVERED_REQUIREMENT_TAG },
72
+ };
73
+ function extractionErrorOf(absolutePath) {
74
+ try {
75
+ extractFromMarkdown(absolutePath);
76
+ return null;
77
+ }
78
+ catch (error) {
79
+ return error instanceof Error ? error.message : String(error);
80
+ }
81
+ }
53
82
  function statusOf(content) {
54
83
  const slice = sliceFrontmatter(content);
55
84
  return slice === null ? undefined : readTopLevelField(slice.text, "status");
@@ -73,12 +102,15 @@ export function planRequirementStatusMappings(workspaceRoot) {
73
102
  const requirementId = typeof id === "string" && id.length > 0
74
103
  ? id
75
104
  : path.basename(file.relativePath, ".md");
76
- const suggestedStatus = SUGGESTED_REQUIREMENT_STATUS[status] ?? null;
105
+ const automatic = DELIVERED_REQUIREMENT_STATUS[status] ?? null;
106
+ const suggestedStatus = automatic?.status ?? SUGGESTED_REQUIREMENT_STATUS[status] ?? null;
77
107
  findings.push({
78
108
  requirementId,
79
109
  path: file.relativePath,
80
110
  status,
81
111
  suggestedStatus,
112
+ automatic,
113
+ extractionError: extractionErrorOf(file.absolutePath),
82
114
  latentDrift: suggestedStatus === null
83
115
  ? null
84
116
  : (planInventoryRederivation(workspaceRoot, requirementId, {
@@ -88,18 +120,54 @@ export function planRequirementStatusMappings(workspaceRoot) {
88
120
  }
89
121
  return findings.sort((left, right) => left.requirementId.localeCompare(right.requirementId));
90
122
  }
123
+ function hasTag(content, tag) {
124
+ const slice = sliceFrontmatter(content);
125
+ const tags = slice === null ? undefined : readTopLevelField(slice.text, "tags");
126
+ return Array.isArray(tags) && tags.includes(tag);
127
+ }
91
128
  /**
92
129
  * Set the status of one requirement from `from` to `to`, replacing only its
93
- * `status` line. Refuses when the authored status is no longer `from`.
130
+ * `status` line, and add `tag` when given and missing. Refuses when the
131
+ * authored status is no longer `from`.
94
132
  */
95
133
  // implements REQ-cli-schema-migration
96
- export function applyRequirementStatusMapping(workspaceRoot, relativePath, from, to) {
134
+ export function applyRequirementStatusMapping(workspaceRoot, relativePath, from, to, tag) {
97
135
  const absolute = path.join(workspaceRoot, relativePath);
98
136
  const content = readText(absolute);
99
137
  if (content === null || statusOf(content) !== from)
100
138
  throw new Error(`${relativePath} no longer has status ${from}; rerun kibi migrate and approve the new plan.`);
101
- const next = withTopLevelField(content, "status", to);
139
+ const mapped = withTopLevelField(content, "status", to);
140
+ const next = mapped === null || tag === undefined || hasTag(mapped, tag)
141
+ ? mapped
142
+ : withAddedTag(mapped, tag);
102
143
  if (next === null)
103
- throw new Error(`${relativePath}: status could not be set to ${to} without changing other frontmatter fields; set it by hand.`);
144
+ throw new Error(`${relativePath}: status could not be set to ${to}${tag === undefined ? "" : ` with tag ${tag}`} without changing other frontmatter fields; set it by hand.`);
104
145
  writeFileAtomically(absolute, next);
105
146
  }
147
+ /**
148
+ * Map every requirement whose status is delivery vocabulary to `closed` with
149
+ * the tag `implemented`, front matter only. With `only`, maps just those
150
+ * requirement ids (a plan action applies what it planned). Idempotent.
151
+ */
152
+ // implements REQ-cli-schema-migration
153
+ export function applyDeliveredStatusMappings(workspaceRoot, only) {
154
+ const mapped = [];
155
+ const skipped = [];
156
+ for (const finding of planRequirementStatusMappings(workspaceRoot)) {
157
+ if (finding.automatic === null)
158
+ continue;
159
+ if (only !== undefined && !only.has(finding.requirementId))
160
+ continue;
161
+ try {
162
+ applyRequirementStatusMapping(workspaceRoot, finding.path, finding.status, finding.automatic.status, finding.automatic.tag);
163
+ mapped.push(finding);
164
+ }
165
+ catch (error) {
166
+ skipped.push({
167
+ path: finding.path,
168
+ reason: error instanceof Error ? error.message : String(error),
169
+ });
170
+ }
171
+ }
172
+ return { mapped, skipped };
173
+ }
@@ -17,16 +17,20 @@ export declare const SEMANTIC_INVENTORY_REVIEW_CODE = "semantic_inventory_review
17
17
  export declare const SEMANTIC_INVENTORY_REDERIVE_DROP_GROUNDING_CODE = "semantic_inventory_rederive_drop_grounding";
18
18
  /** Review, applied only when approved by id: map a legacy requirement status. */
19
19
  export declare const REQ_STATUS_VOCABULARY_MAP_CODE = "req_status_vocabulary_map";
20
+ /** Automatic: map delivery statuses to `closed` plus the tag `implemented`. */
21
+ export declare const REQ_STATUS_DELIVERED_MAP_CODE = "req_status_delivered_map";
22
+ export declare const REQ_STATUS_DELIVERED_MAP_ACTION_ID = "req-status-delivered-map";
20
23
  /** Automatic: stamp `origin: {kind: migration}` on authored entities. */
21
24
  export declare const ENTITY_ORIGIN_BACKFILL_CODE = "entity_origin_backfill";
22
25
  export declare const ENTITY_ORIGIN_BACKFILL_ACTION_ID = "entity-origin-backfill";
23
26
  export { CLOSE_SUPERSEDED_REQUIREMENTS_CODE, MIGRATION_SYNC_ACTION_ID, MIGRATION_SYNC_CODE, SOURCE_PATH_REWRITE_CODE, };
24
27
  /**
25
- * One action per requirement whose status is outside the vocabulary: a
26
- * review (the target status is a semantic choice) carrying the suggested
27
- * mapping and the inventory drift the requirement shows once mapped. With a
28
- * suggestion it is executable when approved by id; the re-derivation of the
29
- * latent drift depends on it.
28
+ * Requirements whose status is outside the vocabulary. Delivery vocabulary
29
+ * gets one automatic action (see deliveredStatusAction). Every other status
30
+ * gets one review action (the target status is a semantic choice) carrying
31
+ * the suggested mapping and the inventory drift the requirement shows once
32
+ * mapped. With a suggestion it is executable when approved by id; the
33
+ * re-derivation of the latent drift depends on it.
30
34
  */
31
35
  export declare function requirementStatusActions(workspaceRoot: string): MigrationAction[];
32
36
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"schema6.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/schema6.ts"],"names":[],"mappings":"AAwBA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAEL,kCAAkC,EAClC,wBAAwB,EACxB,mBAAmB,EAEnB,wBAAwB,EAOzB,MAAM,kDAAkD,CAAC;AAyD1D,gEAAgE;AAEhE,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,+EAA+E;AAE/E,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,4DAA4D;AAE5D,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,yEAAyE;AAEzE,eAAO,MAAM,gCAAgC,gCAAgC,CAAC;AAC9E,gEAAgE;AAEhE,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAC1E;;;GAGG;AAEH,eAAO,MAAM,+CAA+C,+CACd,CAAC;AAC/C,iFAAiF;AAEjF,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;AAgLF;;;;;;GAMG;AAEH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,GACpB,eAAe,EAAE,CAqEnB;AAyBD;;;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,CAuP3B;AAED;;;;;;GAMG;AAEH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG;IAClE,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAuCA;AAED;;;GAGG;AAEH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,GACpB,eAAe,GAAG,IAAI,CAqBxB;AAED,qEAAqE;AAErE,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,eAAe,CAEjB;AAgDD,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,CAoHf;AAED;;;GAGG;AAEH,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,MAAM,CAK9D,CAAC;AAGH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAYtD,CAAC"}
1
+ {"version":3,"file":"schema6.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/schema6.ts"],"names":[],"mappings":"AAwBA,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAEL,kCAAkC,EAClC,wBAAwB,EACxB,mBAAmB,EAEnB,wBAAwB,EAOzB,MAAM,kDAAkD,CAAC;AAkE1D,gEAAgE;AAEhE,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,+EAA+E;AAE/E,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAE7C,4DAA4D;AAE5D,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,yEAAyE;AAEzE,eAAO,MAAM,gCAAgC,gCAAgC,CAAC;AAC9E,gEAAgE;AAEhE,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAC1E;;;GAGG;AAEH,eAAO,MAAM,+CAA+C,+CACd,CAAC;AAC/C,iFAAiF;AAEjF,eAAO,MAAM,8BAA8B,8BAA8B,CAAC;AAC1E,+EAA+E;AAE/E,eAAO,MAAM,6BAA6B,6BAA6B,CAAC;AAExE,eAAO,MAAM,kCAAkC,6BAA6B,CAAC;AAC7E,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;AAiNF;;;;;;;GAOG;AAEH,wBAAgB,wBAAwB,CACtC,aAAa,EAAE,MAAM,GACpB,eAAe,EAAE,CAkFnB;AAyBD;;;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,CA+P3B;AAED;;;;;;GAMG;AAEH,wBAAgB,2BAA2B,CAAC,aAAa,EAAE,MAAM,GAAG;IAClE,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,sBAAsB,EAAE,MAAM,EAAE,CAAC;CAClC,CAuCA;AAED;;;GAGG;AAEH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,GACpB,eAAe,GAAG,IAAI,CAuBxB;AAED,qEAAqE;AAErE,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,eAAe,CAEjB;AAgDD,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,CA0If;AAED;;;GAGG;AAEH,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,MAAM,CAK9D,CAAC;AAGH,eAAO,MAAM,uBAAuB,EAAE,WAAW,CAAC,MAAM,CAatD,CAAC"}
@@ -21,16 +21,18 @@ import { FIRMNESS_FROM_ORIGIN_SCHEMA_VERSION } from "../../public/operations/dec
21
21
  import { migrationAction, } from "../../public/operations/migration-plan.js";
22
22
  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";
23
23
  import { readAllShards } from "../../relationships/shards.js";
24
+ import { KB_PATHS } from "../../utils/kb-paths.js";
24
25
  import { CONTEXT_MISSING_TAG_CODE, SEMANTIC_TEXT_PIN_CODE, applyContextMissingTags, applySemanticTextPins, countByType, planContextMissingTags, planSemanticTextPins, } from "./entity-body-context.js";
25
26
  import { FIRM_SUPERSEDE_ACKNOWLEDGE_CODE, applyFirmSupersedeAcknowledgement, planFirmSupersedeAcknowledgement, } from "./firm-supersede.js";
26
27
  import { applyInventoryRederivation, applyInventoryRederivationDroppingGrounding, planInventoryRederivation, } from "./inventory-rederive.js";
27
28
  import { listLaneMarkdownFiles } from "./kb-sources.js";
28
29
  import { applyOriginBackfill, planOriginBackfill } from "./origin-backfill.js";
29
30
  import { POLARITY_VALUE_BACKFILL_CODE, applyPolarityValueBackfill, planPolarityValueBackfill, } from "./polarity-values.js";
30
- import { applyRequirementStatusMapping, planRequirementStatusMappings, } from "./requirement-status.js";
31
+ import { DELIVERED_REQUIREMENT_TAG, applyDeliveredStatusMappings, applyRequirementStatusMapping, planRequirementStatusMappings, } from "./requirement-status.js";
31
32
  import { applySourcePathRepairs, findSourceRepairs, plannedSourcePathRepairs, } from "./source-paths.js";
32
33
  import { applySupersededClosures, planSupersededClosures, } from "./superseded-closure.js";
33
34
  import { SUPERSEDES_REASON_ACKNOWLEDGE_CODE, applySupersedesReasonAcknowledgement, planSupersedesReasonAcknowledgement, } from "./supersedes-reason.js";
35
+ import { coordinateRefreshFailureMessage, readGeneratedManifestBytes, refreshCoordinatesAfterMigration, } from "./symbol-coordinates-refresh.js";
34
36
  import { SYMBOL_LEGACY_LINK_MARK_ACTION_ID, SYMBOL_LEGACY_LINK_MARK_CODE, applySymbolLegacyLinks, planSymbolLegacyLinks, } from "./symbol-legacy-links.js";
35
37
  /** The KB schema generation that introduced entity `origin`. */
36
38
  // implements REQ-kibi-schema6-migration, REQ-cli-schema-migration
@@ -56,6 +58,11 @@ export const SEMANTIC_INVENTORY_REDERIVE_DROP_GROUNDING_CODE = "semantic_invento
56
58
  /** Review, applied only when approved by id: map a legacy requirement status. */
57
59
  // implements REQ-cli-schema-migration
58
60
  export const REQ_STATUS_VOCABULARY_MAP_CODE = "req_status_vocabulary_map";
61
+ /** Automatic: map delivery statuses to `closed` plus the tag `implemented`. */
62
+ // implements REQ-cli-schema-migration
63
+ export const REQ_STATUS_DELIVERED_MAP_CODE = "req_status_delivered_map";
64
+ // implements REQ-cli-schema-migration
65
+ export const REQ_STATUS_DELIVERED_MAP_ACTION_ID = "req-status-delivered-map";
59
66
  /** Automatic: stamp `origin: {kind: migration}` on authored entities. */
60
67
  // implements REQ-kibi-schema6-migration, REQ-cli-schema-migration
61
68
  export const ENTITY_ORIGIN_BACKFILL_CODE = "entity_origin_backfill";
@@ -210,16 +217,52 @@ function plannedDrops(action) {
210
217
  : []);
211
218
  }
212
219
  /**
213
- * One action per requirement whose status is outside the vocabulary: a
214
- * review (the target status is a semantic choice) carrying the suggested
215
- * mapping and the inventory drift the requirement shows once mapped. With a
216
- * suggestion it is executable when approved by id; the re-derivation of the
217
- * latent drift depends on it.
220
+ * The automatic action for requirements whose status is delivery vocabulary
221
+ * (`implemented`, `done`, `completed`): sync cannot compile them, and their
222
+ * one lossless reading is `closed` plus the tag `implemented`.
223
+ */
224
+ function deliveredStatusAction(findings) {
225
+ return migrationAction({
226
+ id: REQ_STATUS_DELIVERED_MAP_ACTION_ID,
227
+ code: REQ_STATUS_DELIVERED_MAP_CODE,
228
+ category: "schema",
229
+ safety: "automatic",
230
+ autoApplicable: true,
231
+ invocation: { kind: "cli", command_argv: ["kibi", "migrate", "--yes"] },
232
+ affectedEntityIds: findings.map((finding) => finding.requirementId),
233
+ affectedFiles: findings.map((finding) => finding.path),
234
+ postconditions: [{ rule: "req-status-vocabulary", findings: 0 }],
235
+ evidence: {
236
+ reason: "Delivery statuses are outside the entity schema, so kibi sync cannot compile these requirements and every store-backed tool misses them. Each becomes status closed with the tag implemented, which Kibi reads as a delivered requirement that still owes coverage; only the status line and the tags change.",
237
+ count: findings.length,
238
+ mappings: findings.map((finding) => ({
239
+ requirementId: finding.requirementId,
240
+ path: finding.path,
241
+ from: finding.status,
242
+ to: finding.automatic?.status ?? "closed",
243
+ tag: finding.automatic?.tag ?? DELIVERED_REQUIREMENT_TAG,
244
+ })),
245
+ },
246
+ });
247
+ }
248
+ /**
249
+ * Requirements whose status is outside the vocabulary. Delivery vocabulary
250
+ * gets one automatic action (see deliveredStatusAction). Every other status
251
+ * gets one review action (the target status is a semantic choice) carrying
252
+ * the suggested mapping and the inventory drift the requirement shows once
253
+ * mapped. With a suggestion it is executable when approved by id; the
254
+ * re-derivation of the latent drift depends on it.
218
255
  */
219
256
  // implements REQ-cli-schema-migration, REQ-kibi-proposition-complete-ingestion
220
257
  export function requirementStatusActions(workspaceRoot) {
221
258
  const actions = [];
222
- for (const finding of planRequirementStatusMappings(workspaceRoot)) {
259
+ const findings = planRequirementStatusMappings(workspaceRoot);
260
+ const delivered = findings.filter((finding) => finding.automatic !== null);
261
+ if (delivered.length > 0)
262
+ actions.push(deliveredStatusAction(delivered));
263
+ for (const finding of findings) {
264
+ if (finding.automatic !== null)
265
+ continue;
223
266
  const id = `req-status-vocabulary-map-${finding.requirementId}`;
224
267
  const drift = finding.latentDrift;
225
268
  const target = finding.suggestedStatus;
@@ -232,7 +275,9 @@ export function requirementStatusActions(workspaceRoot) {
232
275
  invocation: target === null
233
276
  ? {
234
277
  kind: "review",
235
- instruction: `${finding.requirementId} (${finding.path}) has status ${finding.status}, which is not a requirement status, so it is skipped by the proof ladder and its semantic inventory is never validated. Set status to open, in_progress or closed (legacy: active, approved) in the file, or use proof_exempt to park it, then rerun kibi migrate.`,
278
+ instruction: finding.extractionError === null
279
+ ? `${finding.requirementId} (${finding.path}) has status ${finding.status}, which is not a requirement status, so it is skipped by the proof ladder and its semantic inventory is never validated. Set status to open, in_progress or closed (legacy: active, approved) in the file, or use proof_exempt to park it, then rerun kibi migrate.`
280
+ : `${finding.requirementId} (${finding.path}) has status ${finding.status}, which is not a status Kibi knows, so kibi sync cannot compile it (${finding.extractionError}) and kb_query, kb_coverage and kb_upsert do not see it. Set status to open, in_progress or closed in the file (closed with the tag implemented for delivered work), then run kibi sync and rerun kibi migrate.`,
236
281
  }
237
282
  : { kind: "cli", command_argv: approvalArgv([id]) },
238
283
  affectedEntityIds: [finding.requirementId],
@@ -241,9 +286,14 @@ export function requirementStatusActions(workspaceRoot) {
241
286
  dispositionRequired: true,
242
287
  allowedDispositions: ["fixed", "deferred"],
243
288
  evidence: {
244
- reason: `Status ${finding.status} is ADR or lifecycle vocabulary, not a requirement status: the requirement is skipped by the proof ladder and kibi sync never validates its semantic inventory.`,
289
+ reason: finding.extractionError === null
290
+ ? `Status ${finding.status} is ADR or lifecycle vocabulary, not a requirement status: the requirement is skipped by the proof ladder and kibi sync never validates its semantic inventory.`
291
+ : `Status ${finding.status} is outside the entity schema, so kibi sync cannot compile the requirement at all.`,
245
292
  status: finding.status,
246
293
  suggestedStatus: target,
294
+ ...(finding.extractionError === null
295
+ ? {}
296
+ : { extractionError: finding.extractionError }),
247
297
  ...(target === null
248
298
  ? {}
249
299
  : {
@@ -515,9 +565,13 @@ export function buildSchema6MigrationFragment(input) {
515
565
  if (!plan.safe && plan.rederivableByDroppingGrounding)
516
566
  actions.push(dropGroundingAction(plan));
517
567
  }
518
- // Requirements with a status outside the vocabulary: mapping is a review,
519
- // and their latent drift is re-derived only after an approved mapping.
520
- actions.push(...requirementStatusActions(input.workspaceRoot));
568
+ // Requirements with a status outside the vocabulary: delivery statuses are
569
+ // mapped automatically; any other mapping is a review, and its latent drift
570
+ // is re-derived only after an approved mapping.
571
+ const statusActions = requirementStatusActions(input.workspaceRoot);
572
+ actions.push(...statusActions);
573
+ if (statusActions.some((action) => action.id === REQ_STATUS_DELIVERED_MAP_ACTION_ID))
574
+ sourceRewriteActionIds.push(REQ_STATUS_DELIVERED_MAP_ACTION_ID);
521
575
  if (drift.some((plan) => !plan.safe)) {
522
576
  diagnostics.push(`${drift.filter((plan) => !plan.safe).length} requirement inventory(ies) need a manual fix before kibi sync succeeds; see the semantic_inventory_review actions.`);
523
577
  }
@@ -637,7 +691,9 @@ export function symbolLegacyLinkAction(workspaceRoot) {
637
691
  autoApplicable: true,
638
692
  invocation: { kind: "cli", command_argv: ["kibi", "migrate", "--yes"] },
639
693
  affectedEntityIds: plan.targets.map((target) => target.id),
640
- affectedFiles: [plan.manifestPath],
694
+ // A mark changes the record's coordinate identity, so the action also
695
+ // regenerates the coordinate artifact.
696
+ affectedFiles: [plan.manifestPath, KB_PATHS.symbolCoordinates],
641
697
  postconditions: [{ rule: "symbol_granularity_violation", findings: 0 }],
642
698
  evidence: {
643
699
  reason: "These symbols link a source file coarsely while it declares narrower behavioral symbols, so the staged check (kibi check --staged) blocks the next commit that touches them. Each gets granularity_reason: legacy-link (accepted legacy granularity); no link moves and no other field changes.",
@@ -725,6 +781,12 @@ export async function applySchema6MigrationAction(action, context) {
725
781
  applyRequirementStatusMapping(context.workspaceRoot, mapping.path, mapping.from, mapping.to);
726
782
  return;
727
783
  }
784
+ case REQ_STATUS_DELIVERED_MAP_CODE: {
785
+ const result = applyDeliveredStatusMappings(context.workspaceRoot, new Set(action.affectedEntityIds));
786
+ if (result.skipped.length > 0)
787
+ throw new Error(`Could not map the status of ${result.skipped.map((skip) => `${skip.path} (${skip.reason})`).join(", ")}; set status: closed and add the tag implemented by hand.`);
788
+ return;
789
+ }
728
790
  case SEMANTIC_INVENTORY_REDERIVE_DROP_GROUNDING_CODE: {
729
791
  const requirementId = action.affectedEntityIds[0];
730
792
  const contractHash = action.evidence.contractHash;
@@ -737,6 +799,7 @@ export async function applySchema6MigrationAction(action, context) {
737
799
  applyOriginBackfill(context.workspaceRoot, context.clock().toISOString());
738
800
  return;
739
801
  case SYMBOL_LEGACY_LINK_MARK_CODE: {
802
+ const before = readGeneratedManifestBytes(context.workspaceRoot);
740
803
  const result = applySymbolLegacyLinks(context.workspaceRoot, {
741
804
  ...(typeof action.evidence.manifestHash === "string"
742
805
  ? { expectedHash: action.evidence.manifestHash }
@@ -745,6 +808,11 @@ export async function applySchema6MigrationAction(action, context) {
745
808
  });
746
809
  if (result.unmarked.length > 0)
747
810
  throw new Error(`Could not mark ${result.unmarked.join(", ")} as legacy-link in place; add granularity_reason: legacy-link to them by hand.`);
811
+ // A legacy-link mark changes the record's coordinate identity; keep
812
+ // the generated artifact current so the next commit carries no drift.
813
+ const refresh = await refreshCoordinatesAfterMigration(context.workspaceRoot, before);
814
+ if (refresh.outcome === "failed")
815
+ console.warn(`Warning: ${coordinateRefreshFailureMessage(refresh.reason)}`);
748
816
  return;
749
817
  }
750
818
  case CLOSE_SUPERSEDED_REQUIREMENTS_CODE: {
@@ -791,6 +859,7 @@ export const SCHEMA6_AUTOMATIC_CODES = new Set([
791
859
  SUPERSEDES_REASON_ACKNOWLEDGE_CODE,
792
860
  FIRM_SUPERSEDE_ACKNOWLEDGE_CODE,
793
861
  SEMANTIC_INVENTORY_REDERIVE_CODE,
862
+ REQ_STATUS_DELIVERED_MAP_CODE,
794
863
  ENTITY_ORIGIN_BACKFILL_CODE,
795
864
  CLOSE_SUPERSEDED_REQUIREMENTS_CODE,
796
865
  SOURCE_PATH_REWRITE_CODE,
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Bytes of the symbols manifest and its generated coordinate artifact at
3
+ * their canonical paths; null for a file that does not exist.
4
+ */
5
+ export type GeneratedManifestBytes = Readonly<{
6
+ symbols: string | null;
7
+ coordinates: string | null;
8
+ }>;
9
+ /** Snapshot taken before a migration writes, to tell what it rewrote. */
10
+ export declare function readGeneratedManifestBytes(workspaceRoot: string): GeneratedManifestBytes;
11
+ /** What the post-migration coordinate refresh did. */
12
+ export type MigrationCoordinateRefresh = {
13
+ outcome: "skipped";
14
+ } | {
15
+ outcome: "refreshed";
16
+ } | {
17
+ outcome: "failed";
18
+ reason: string;
19
+ };
20
+ /**
21
+ * Refresh generated symbol coordinates the way
22
+ * `kibi sync --refresh-symbol-coordinates` does, after a migration rewrote or
23
+ * moved the symbols manifest. The coordinate artifact binds each record by an
24
+ * identity hash that includes `granularity_reason`, so a legacy-link mark
25
+ * leaves the committed coordinates stale; without this refresh the pre-commit
26
+ * generated-manifest gate rejects the first commit after a migration.
27
+ *
28
+ * Skipped when `before` shows the run left both files untouched, or when the
29
+ * manifest has no symbols. A refresh failure is returned, not thrown: the
30
+ * migration itself is complete, and the caller tells the user to refresh by
31
+ * hand before committing.
32
+ */
33
+ export declare function refreshCoordinatesAfterMigration(workspaceRoot: string, before?: GeneratedManifestBytes): Promise<MigrationCoordinateRefresh>;
34
+ /** The line a migration prints when its coordinate refresh failed. */
35
+ export declare function coordinateRefreshFailureMessage(reason: string): string;
36
+ //# sourceMappingURL=symbol-coordinates-refresh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbol-coordinates-refresh.d.ts","sourceRoot":"","sources":["../../../src/operations/migration/symbol-coordinates-refresh.ts"],"names":[],"mappings":"AA0BA;;;GAGG;AAEH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC,CAAC;AAMH,yEAAyE;AAEzE,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,GACpB,sBAAsB,CAMxB;AAYD,sDAAsD;AAEtD,MAAM,MAAM,0BAA0B,GAClC;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GACxB;IAAE,OAAO,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1C;;;;;;;;;;;;GAYG;AAEH,wBAAsB,gCAAgC,CACpD,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,sBAAsB,GAC9B,OAAO,CAAC,0BAA0B,CAAC,CA0CrC;AAED,sEAAsE;AAEtE,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE"}
@@ -0,0 +1,99 @@
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 { createHash } from "node:crypto";
19
+ import { existsSync, readFileSync } from "node:fs";
20
+ import path from "node:path";
21
+ import { load as parseYAML } from "js-yaml";
22
+ import { resolveSymbolsManifestPaths } from "../../utils/manifest-paths.js";
23
+ import { republishPendingSourceReceipt } from "../mutation/source-authoring.js";
24
+ import { withSymbolCompilerLock } from "../mutation/symbol-compiler-lock.js";
25
+ function readOrNull(filePath) {
26
+ return existsSync(filePath) ? readFileSync(filePath, "utf8") : null;
27
+ }
28
+ /** Snapshot taken before a migration writes, to tell what it rewrote. */
29
+ // implements REQ-cli-migration-first-commit
30
+ export function readGeneratedManifestBytes(workspaceRoot) {
31
+ const paths = resolveSymbolsManifestPaths(workspaceRoot);
32
+ return {
33
+ symbols: readOrNull(paths.symbolsPath),
34
+ coordinates: readOrNull(paths.coordinatesPath),
35
+ };
36
+ }
37
+ function hasSymbols(content) {
38
+ try {
39
+ const parsed = parseYAML(content);
40
+ return Array.isArray(parsed?.symbols) && parsed.symbols.length > 0;
41
+ }
42
+ catch {
43
+ // An unreadable manifest is reported by the refresh itself.
44
+ return true;
45
+ }
46
+ }
47
+ /**
48
+ * Refresh generated symbol coordinates the way
49
+ * `kibi sync --refresh-symbol-coordinates` does, after a migration rewrote or
50
+ * moved the symbols manifest. The coordinate artifact binds each record by an
51
+ * identity hash that includes `granularity_reason`, so a legacy-link mark
52
+ * leaves the committed coordinates stale; without this refresh the pre-commit
53
+ * generated-manifest gate rejects the first commit after a migration.
54
+ *
55
+ * Skipped when `before` shows the run left both files untouched, or when the
56
+ * manifest has no symbols. A refresh failure is returned, not thrown: the
57
+ * migration itself is complete, and the caller tells the user to refresh by
58
+ * hand before committing.
59
+ */
60
+ // implements REQ-cli-migration-first-commit
61
+ export async function refreshCoordinatesAfterMigration(workspaceRoot, before) {
62
+ const after = readGeneratedManifestBytes(workspaceRoot);
63
+ if (after.symbols === null || !hasSymbols(after.symbols))
64
+ return { outcome: "skipped" };
65
+ if (before !== undefined &&
66
+ before.symbols === after.symbols &&
67
+ before.coordinates === after.coordinates)
68
+ return { outcome: "skipped" };
69
+ const { symbolsPath, coordinatesPath } = resolveSymbolsManifestPaths(workspaceRoot);
70
+ try {
71
+ const { refreshManifestCoordinates } = await import("../../commands/sync/manifest.js");
72
+ await withSymbolCompilerLock(workspaceRoot, () => refreshManifestCoordinates(symbolsPath, workspaceRoot, {
73
+ refreshSymbolCoordinates: true,
74
+ }));
75
+ // Files the migration moved are still untracked and pinned by a
76
+ // pending-source receipt; rebind each to its refreshed bytes so the next
77
+ // sync does not refuse it as hash drift. A file without a receipt is
78
+ // left alone.
79
+ for (const filePath of [symbolsPath, coordinatesPath]) {
80
+ if (!existsSync(filePath))
81
+ continue;
82
+ republishPendingSourceReceipt(workspaceRoot, path.relative(workspaceRoot, filePath).split(path.sep).join("/"), createHash("sha256")
83
+ .update(readFileSync(filePath, "utf8"), "utf8")
84
+ .digest("hex"));
85
+ }
86
+ return { outcome: "refreshed" };
87
+ }
88
+ catch (error) {
89
+ return {
90
+ outcome: "failed",
91
+ reason: error instanceof Error ? error.message : String(error),
92
+ };
93
+ }
94
+ }
95
+ /** The line a migration prints when its coordinate refresh failed. */
96
+ // implements REQ-cli-migration-first-commit
97
+ export function coordinateRefreshFailureMessage(reason) {
98
+ return `Could not refresh symbol coordinates after the migration (${reason}). Run 'kibi sync --refresh-symbol-coordinates' and stage .kb/symbols.yaml and .kb/symbol-coordinates.yaml before the first commit, or the pre-commit hook reports generated manifest drift.`;
99
+ }
@@ -43,6 +43,15 @@ export declare function normalizeAuthoredSourcePath(workspaceRoot: string, sourc
43
43
  */
44
44
  export declare function configuredSourceTarget(workspaceRoot: string, type: string): string | undefined;
45
45
  export declare function hasConfiguredSourceTarget(workspaceRoot: string, type: string): boolean;
46
+ /**
47
+ * The absolute authored document an upsert of an entity the store does not
48
+ * hold would write: the same resolution writeSourceForUpsert uses, so a check
49
+ * on that file judges the file the writer merges into. Undefined when the
50
+ * writer has no target (it reports DOCUMENT_PATH_REQUIRED itself).
51
+ */
52
+ export declare function newEntitySourceTarget(context: OperationContext, input: UpsertInput): string | undefined;
53
+ /** The front matter of an authored document; throws on YAML it cannot parse. */
54
+ export declare function authoredFrontmatter(content: string): Record<string, unknown>;
46
55
  /** Generated coordinate fields belong exclusively to `.kb/symbol-coordinates.yaml`. */
47
56
  export declare const GENERATED_SYMBOL_FIELDS: readonly ["sourceLine", "sourceColumn", "sourceEndLine", "sourceEndColumn", "coordinatesGeneratedAt"];
48
57
  export declare function manifestRelationships(item: unknown): Array<{
@@ -1 +1 @@
1
- {"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,7 @@ import { isDeepStrictEqual } from "node:util";
7
7
  import { dump as dumpYaml, load as loadYaml } from "js-yaml";
8
8
  import { isCollection, isMap, isNode, isScalar, parseDocument, stringify, } from "yaml";
9
9
  import { OperationError } from "../../cli-errors.js";
10
+ import { reviseRequirementBody } from "../../entity-body-context.js";
10
11
  import { requirementSemanticText } from "../../extractors/markdown.js";
11
12
  import { canonicalFilesystemPath } from "../../utils/canonical-path.js";
12
13
  import { CANONICAL_ENTITY_PATHS, isDerivedKbPath, } from "../../utils/kb-paths.js";
@@ -248,6 +249,26 @@ function sourcePath(context, input, entity, existing) {
248
249
  }
249
250
  return resolveContainedSourcePath(context.workspaceRoot, relative);
250
251
  }
252
+ /**
253
+ * The absolute authored document an upsert of an entity the store does not
254
+ * hold would write: the same resolution writeSourceForUpsert uses, so a check
255
+ * on that file judges the file the writer merges into. Undefined when the
256
+ * writer has no target (it reports DOCUMENT_PATH_REQUIRED itself).
257
+ */
258
+ // implements REQ-kibi-uncompiled-source-visibility
259
+ export function newEntitySourceTarget(context, input) {
260
+ try {
261
+ return sourcePath(context, input, { id: input.id }, undefined);
262
+ }
263
+ catch {
264
+ return undefined;
265
+ }
266
+ }
267
+ /** The front matter of an authored document; throws on YAML it cannot parse. */
268
+ // implements REQ-kibi-uncompiled-source-visibility
269
+ export function authoredFrontmatter(content) {
270
+ return bodyAndFrontmatter(content).frontmatter;
271
+ }
251
272
  /**
252
273
  * Front matter keys an upsert never writes: runtime provenance/timestamps
253
274
  * belong to the compiled entity, not to the authored source artifact, and
@@ -347,6 +368,33 @@ function patchFrontmatter(raw, loaded, updates) {
347
368
  }
348
369
  return patched;
349
370
  }
371
+ // implements REQ-kb-upsert-statement-follows-semantic-text
372
+ function sameStatement(left, right) {
373
+ const normalized = (text) => text.replace(/\s+/g, " ").trim();
374
+ return normalized(left) === normalized(right);
375
+ }
376
+ /**
377
+ * The body an upsert without `document.body` keeps. A requirement whose
378
+ * `semantic_text` changes gets the new statement in place of the old one
379
+ * (context sections are kept), so search and readers of the document never
380
+ * quote a statement the requirement no longer makes.
381
+ */
382
+ // implements REQ-kb-upsert-statement-follows-semantic-text
383
+ function bodyForStatement(type, entity, existing) {
384
+ if (type !== "req" || typeof entity.semantic_text !== "string") {
385
+ return existing.body;
386
+ }
387
+ const previous = typeof existing.frontmatter.semantic_text === "string"
388
+ ? existing.frontmatter.semantic_text
389
+ : requirementSemanticText(existing.body);
390
+ if (sameStatement(previous, entity.semantic_text))
391
+ return existing.body;
392
+ const leading = /^\n*/.exec(existing.body)?.[0] || "\n\n";
393
+ return `${leading}${reviseRequirementBody({
394
+ existingBody: existing.body,
395
+ statement: entity.semantic_text,
396
+ })}`;
397
+ }
350
398
  function renderEntityDocument(input, entity, existingContent) {
351
399
  const existing = existingContent
352
400
  ? bodyAndFrontmatter(existingContent)
@@ -354,7 +402,7 @@ function renderEntityDocument(input, entity, existingContent) {
354
402
  const body = input.document?.body !== undefined
355
403
  ? input.document.body
356
404
  : existingContent !== undefined
357
- ? existing.body
405
+ ? bodyForStatement(input.type, entity, existing)
358
406
  : input.type === "req"
359
407
  ? `${String(entity.semantic_text ?? "").trim()}\n`
360
408
  : "\n";
@@ -790,7 +838,7 @@ export function renderYamlRelationshipDeletion(sourcePathValue, existingContent,
790
838
  * document's current content. Compile plans render their documents with it
791
839
  * so a plan writes exactly what `kb_upsert` would.
792
840
  */
793
- // implements REQ-kibi-change-to-proof-plan-compiler-v2
841
+ // implements REQ-kibi-change-to-proof-plan-compiler-v2, REQ-kb-upsert-statement-follows-semantic-text
794
842
  export function renderSourceDocument(input, entity, existingContent, relativePath) {
795
843
  const extension = path.extname(relativePath).toLowerCase();
796
844
  if (extension === ".yaml" || extension === ".yml") {
@@ -9,6 +9,11 @@ export type StoredEntityLookup = {
9
9
  readonly exists: () => Promise<boolean>;
10
10
  /** The stored entity, loaded only when one exists. */
11
11
  readonly entity: () => Promise<StoredEntity>;
12
+ /**
13
+ * The entity's own relationships when they come from its authored file
14
+ * rather than the store (an entity the store does not hold yet).
15
+ */
16
+ readonly relationships?: readonly Readonly<Record<string, unknown>>[];
12
17
  };
13
18
  /**
14
19
  * One read of the entity an upsert replaces, shared by every step of the
@@ -1 +1 @@
1
- {"version":3,"file":"stored-entity.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/stored-entity.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAEhF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;CAC9C,CAAC;AAGF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GACX,kBAAkB,CAsBpB"}
1
+ {"version":3,"file":"stored-entity.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/stored-entity.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAE3E;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAGhF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+CAA+C;IAC/C,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACxC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;CACvE,CAAC;AAGF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,MAAM,GACX,kBAAkB,CAsBpB"}