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,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { logicSemanticKey, utf8Span, validateLogicIr, } from "../../logic/ir.js";
3
3
  import { modeledClaimRole, normalizeSemanticClause, semanticClaimKey, } from "../semantic-advisor/clauses.js";
4
+ import { advisorPropositionRole } from "../semantic-advisor/proposition-role.js";
4
5
  import { semanticSourceHash } from "../semantic-advisor/shared.js";
5
6
  function shortId(value) {
6
7
  return createHash("sha256")
@@ -98,7 +99,8 @@ export function buildLogicApplyPlan(input) {
98
99
  {
99
100
  claim_key: claimKey,
100
101
  claim_text: claimText,
101
- role: modeledClaimRole(claimText, validation.normalized.modality),
102
+ role: advisorPropositionRole(input.text.trim(), claimKey) ??
103
+ modeledClaimRole(claimText, validation.normalized.modality),
102
104
  status: "modeled",
103
105
  span,
104
106
  semantic_key: semanticKey,
@@ -1 +1 @@
1
- {"version":3,"file":"model-requirement.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/model-requirement.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAgBjF,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,iCAAiC,EACjC,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAUhC,YAAY,EACV,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAkNhC,OAAO,EACL,iCAAiC,EACjC,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,GACxB,CAAC;AAIF,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GACA,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4CzB;AA+OD,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,oBAAoB,EAC1B,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,sBAAsB,CAAC,CAgHjC;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,sBAAsB,CAAC,CAajC"}
1
+ {"version":3,"file":"model-requirement.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/model-requirement.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAgBjF,OAAO,EACL,yBAAyB,EACzB,uBAAuB,EACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,iCAAiC,EACjC,uBAAuB,EACxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,wBAAwB,CAAC;AAUhC,YAAY,EACV,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAkNhC,OAAO,EACL,iCAAiC,EACjC,uBAAuB,EACvB,yBAAyB,EACzB,uBAAuB,GACxB,CAAC;AAIF,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE;IACP,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,GACA,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+CzB;AAwOD,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAIxB;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,oBAAoB,EAC1B,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,sBAAsB,CAAC,CAgHjC;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,oBAAoB,EAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,sBAAsB,CAAC,CAajC"}
@@ -1,9 +1,9 @@
1
1
  import { buildStrictWriteSet, } from "../../public/check-types.js";
2
2
  import { readKbManifestStatus } from "../../utils/kb-manifest.js";
3
3
  import { isConventionalSubjectKey, normalizeSubjectKey, } from "../../utils/strict-modeling.js";
4
- import { propositionRole } from "../semantic-advisor/analyze-prose.js";
5
- import { extractSemanticClauses, modeledClaimRole, normalizeSemanticClause, semanticClaimKey, } from "../semantic-advisor/clauses.js";
4
+ import { modeledClaimRole, normalizeSemanticClause, semanticClaimKey, } from "../semantic-advisor/clauses.js";
6
5
  import { detectConditionalRule } from "../semantic-advisor/conditional-rules.js";
6
+ import { advisorPropositionRole } from "../semantic-advisor/proposition-role.js";
7
7
  import { semanticSourceHash } from "../semantic-advisor/shared.js";
8
8
  import { buildLogicApplyPlan } from "./logic-modeling.js";
9
9
  import { strictWriteSetToApplyPlan, writeSetPrimaryEntityId, } from "./requirement-applyplan.js";
@@ -198,7 +198,9 @@ export function annotateModelRequirementStep(step, context) {
198
198
  {
199
199
  claim_key: context.claimKey,
200
200
  claim_text: normalizedClaimText,
201
- role: modeledClaimRole(claimText),
201
+ role: advisorPropositionRole(claimText, context.claimKey, [
202
+ claimText,
203
+ ]) ?? modeledClaimRole(claimText),
202
204
  status: "modeled",
203
205
  span: {
204
206
  start: 0,
@@ -235,10 +237,6 @@ function unresolvedObservationStep(step) {
235
237
  // ingestion compares the persisted inventory with it. A rule modality alone
236
238
  // would call "X must not happen unless C" normative where the advisor reads an
237
239
  // exception, so the role comes from the advisor's own clause analysis.
238
- function advisorRole(semanticText, claimKey) {
239
- const clause = extractSemanticClauses(semanticText).find((entry) => entry.claim_key === claimKey);
240
- return clause ? propositionRole(clause.text, clause.normative) : null;
241
- }
242
240
  function withAdvisorRoles(plan) {
243
241
  return plan.map((step) => {
244
242
  if (step.type !== "req")
@@ -257,7 +255,7 @@ function withAdvisorRoles(plan) {
257
255
  return entry;
258
256
  const row = entry;
259
257
  const role = typeof row.claim_key === "string"
260
- ? advisorRole(semanticText, row.claim_key)
258
+ ? advisorPropositionRole(semanticText, row.claim_key)
261
259
  : null;
262
260
  return role === null ? row : { ...row, role };
263
261
  }),
@@ -351,7 +349,7 @@ async function unresolvedConditionalResult(args, extracted, reason, workspaceRoo
351
349
  {
352
350
  claim_key: claimKey,
353
351
  claim_text: claimText,
354
- role: advisorRole(semanticText, claimKey) ??
352
+ role: advisorPropositionRole(semanticText, claimKey) ??
355
353
  modeledClaimRole(claimText),
356
354
  status: "ontology_gap",
357
355
  span: {
@@ -0,0 +1,13 @@
1
+ import type { UpsertInput } from "./types.js";
2
+ /**
3
+ * The body the authored document has, or will have, after this upsert: the
4
+ * written file when there is one, the supplied body, the preserved body of
5
+ * the entity's existing document, or the default a new requirement gets.
6
+ */
7
+ export declare function upsertDocumentBody(input: UpsertInput, entity: Readonly<Record<string, unknown>>, workspaceRoot: string, writtenPath?: string): string;
8
+ /**
9
+ * kb_upsert warning for a written entity whose body lacks context. The same
10
+ * finding blocks `kb_check` as entity-context-missing, so agents see it first.
11
+ */
12
+ export declare function entityContextWarnings(input: UpsertInput, entity: Readonly<Record<string, unknown>>, workspaceRoot: string, writtenPath?: string): readonly string[];
13
+ //# sourceMappingURL=context-warning.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-warning.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/context-warning.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAgB9C;;;;GAIG;AAEH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAYR;AAED;;;GAGG;AAEH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,SAAS,MAAM,EAAE,CA2BnB"}
@@ -0,0 +1,59 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { contextFinding } from "../../entity-body-context.js";
4
+ import { requirementSemanticText } from "../../extractors/markdown.js";
5
+ import { readAuthoredEntity } from "../../public/operations/entity-context.js";
6
+ import { readKbManifest } from "../../utils/kb-manifest.js";
7
+ function readBody(workspaceRoot, source) {
8
+ if (typeof source !== "string" || !source.endsWith(".md"))
9
+ return undefined;
10
+ try {
11
+ return (readAuthoredEntity(readFileSync(path.resolve(workspaceRoot, source), "utf8"), source)?.body ?? undefined);
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ }
17
+ /**
18
+ * The body the authored document has, or will have, after this upsert: the
19
+ * written file when there is one, the supplied body, the preserved body of
20
+ * the entity's existing document, or the default a new requirement gets.
21
+ */
22
+ // implements REQ-kb-entity-body-context
23
+ export function upsertDocumentBody(input, entity, workspaceRoot, writtenPath) {
24
+ if (writtenPath !== undefined) {
25
+ const written = readBody(workspaceRoot, writtenPath);
26
+ if (written !== undefined)
27
+ return written;
28
+ }
29
+ if (input.document?.body !== undefined)
30
+ return input.document.body;
31
+ const existing = readBody(workspaceRoot, input.document?.path ?? entity.source);
32
+ if (existing !== undefined)
33
+ return existing;
34
+ return input.type === "req" ? `${String(entity.semantic_text ?? "")}\n` : "";
35
+ }
36
+ /**
37
+ * kb_upsert warning for a written entity whose body lacks context. The same
38
+ * finding blocks `kb_check` as entity-context-missing, so agents see it first.
39
+ */
40
+ // implements REQ-kb-entity-body-context
41
+ export function entityContextWarnings(input, entity, workspaceRoot, writtenPath) {
42
+ const body = upsertDocumentBody(input, entity, workspaceRoot, writtenPath);
43
+ const semanticText = typeof entity.semantic_text === "string" && entity.semantic_text.trim()
44
+ ? entity.semantic_text
45
+ : input.type === "req"
46
+ ? requirementSemanticText(body)
47
+ : undefined;
48
+ const acknowledged = (readKbManifest(workspaceRoot)?.contextAcknowledged ?? []).includes(input.id);
49
+ const finding = contextFinding(input.type, input.id, body, {
50
+ title: entity.title,
51
+ semantic_text: semanticText,
52
+ fact_kind: entity.fact_kind,
53
+ }, acknowledged);
54
+ return finding === null
55
+ ? []
56
+ : [
57
+ `${finding.description}. kb_check blocks it under entity-context-missing. ${finding.suggestion}`,
58
+ ];
59
+ }
@@ -1,4 +1,5 @@
1
1
  import type { PrologPort } from "../../public/operations/runtime-types.js";
2
+ import { type StoredEntityLookup } from "./stored-entity.js";
2
3
  import type { StagedUpsertState, UpsertInput } from "./types.js";
3
4
  /**
4
5
  * Resolve the provenance an upsert writes. kb_upsert never overwrites who
@@ -17,5 +18,5 @@ import type { StagedUpsertState, UpsertInput } from "./types.js";
17
18
  * Without a Prolog runtime the existing entity cannot be read, so only rule 1
18
19
  * applies.
19
20
  */
20
- export declare function resolveUpsertOrigin(input: UpsertInput, prolog: Pick<PrologPort, "query"> | undefined, now: Date, staged?: StagedUpsertState): Promise<UpsertInput>;
21
+ export declare function resolveUpsertOrigin(input: UpsertInput, prolog: Pick<PrologPort, "query"> | undefined, now: Date, staged?: StagedUpsertState, lookup?: StoredEntityLookup): Promise<UpsertInput>;
21
22
  //# sourceMappingURL=origin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/origin.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAUjE;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,SAAS,EAC7C,GAAG,EAAE,IAAI,EACT,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CAiCtB"}
1
+ {"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/origin.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAUjE;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,GAAG,SAAS,EAC7C,GAAG,EAAE,IAAI,EACT,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAwBtB"}
@@ -15,9 +15,8 @@
15
15
  You should have received a copy of the GNU Affero General Public License
16
16
  along with this program. If not, see <https://www.gnu.org/licenses/>.
17
17
  */
18
- import { escapeAtom } from "../../prolog/codec.js";
19
18
  import { readEntityOrigin } from "../../public/entity-origin.js";
20
- import { loadEntities } from "../../public/operations/discovery-entities.js";
19
+ import { storedEntityLookup, } from "./stored-entity.js";
21
20
  function isRecord(value) {
22
21
  return value !== null && typeof value === "object" && !Array.isArray(value);
23
22
  }
@@ -42,7 +41,7 @@ function withOrigin(input, origin) {
42
41
  * applies.
43
42
  */
44
43
  // implements REQ-kibi-entity-origin
45
- export async function resolveUpsertOrigin(input, prolog, now, staged) {
44
+ export async function resolveUpsertOrigin(input, prolog, now, staged, lookup) {
46
45
  const supplied = input.properties.origin;
47
46
  if (supplied !== undefined) {
48
47
  return isRecord(supplied) && supplied.recorded_at === undefined
@@ -57,22 +56,12 @@ export async function resolveUpsertOrigin(input, prolog, now, staged) {
57
56
  const stored = readEntityOrigin(planned.origin);
58
57
  return stored === null ? input : withOrigin(input, stored);
59
58
  }
60
- // The same existence probe the rest of the upsert uses.
61
- const exists = await prolog.query(`once(kb_entity('${escapeAtom(input.id)}', _, _))`);
62
- if (!exists.success) {
59
+ const existing = await (lookup ?? storedEntityLookup(prolog, input.id, input.type)).entity();
60
+ if (existing === undefined) {
63
61
  return withOrigin(input, { kind: "agent", recorded_at: now.toISOString() });
64
62
  }
65
- let stored = null;
66
- try {
67
- const [existing] = await loadEntities(prolog, {
68
- id: input.id,
69
- type: input.type,
70
- });
71
- stored = readEntityOrigin(existing?.origin);
72
- }
73
- catch {
74
- // Unreadable: leave origin out. The source-first write merges the
75
- // authored frontmatter, so a stored origin there survives either way.
76
- }
63
+ // Unreadable (null): leave origin out. The source-first write merges the
64
+ // authored frontmatter, so a stored origin there survives either way.
65
+ const stored = readEntityOrigin(existing?.origin);
77
66
  return stored === null ? input : withOrigin(input, stored);
78
67
  }
@@ -0,0 +1,16 @@
1
+ import type { PrologPort } from "../../public/operations/runtime-types.js";
2
+ import { type StoredEntityLookup } from "./stored-entity.js";
3
+ import type { StagedUpsertState, UpsertInput } from "./types.js";
4
+ /**
5
+ * kb_upsert on an existing requirement that changes neither its prose nor its
6
+ * proposition ledger keeps the stored ledger. A payload that adds a
7
+ * relationship (for example `specified_by`) and carries no `semantic_*` or
8
+ * `logic_claims` field, and no `title` or `text_ref` that differs from the
9
+ * stored value, is merged over the stored semantic contract (and the stored
10
+ * `text_ref` it omits) before validation. The
11
+ * proposition-complete check then runs against the merged requirement, which
12
+ * is also what the write records. A payload that supplies any ledger field or
13
+ * changes the prose is validated exactly as given.
14
+ */
15
+ export declare function withStoredRequirementSemantics(input: UpsertInput, prolog: Pick<PrologPort, "query">, staged?: StagedUpsertState, lookup?: StoredEntityLookup): Promise<UpsertInput>;
16
+ //# sourceMappingURL=requirement-semantics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requirement-semantics.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/requirement-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0CAA0C,CAAC;AAC3E,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAwCjE;;;;;;;;;;GAUG;AACH,wBAAsB,8BAA8B,CAClD,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,EACjC,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,GAAE,kBAAqE,GAC5E,OAAO,CAAC,WAAW,CAAC,CA6BtB"}
@@ -0,0 +1,64 @@
1
+ import { storedEntityLookup, } from "./stored-entity.js";
2
+ /** The fields that make up a requirement's proposition ledger. */
3
+ const SEMANTIC_CONTRACT_FIELDS = [
4
+ "semantic_text",
5
+ "semantic_clauses",
6
+ "semantic_inventory",
7
+ "semantic_inventory_version",
8
+ "semantic_source_field",
9
+ "semantic_source_hash",
10
+ "logic_claims",
11
+ ];
12
+ /** Prose a requirement's semantic source can be read from besides semantic_text. */
13
+ const PROSE_FIELDS = ["title", "text_ref"];
14
+ /** Stored prose a relationship-only update may leave out. */
15
+ const PRESERVED_PROSE_FIELDS = ["text_ref"];
16
+ function suppliesSemanticContract(properties) {
17
+ return Object.keys(properties).some((key) => key.startsWith("semantic_") || key === "logic_claims");
18
+ }
19
+ async function storedRequirement(id, lookup, staged) {
20
+ // An earlier step of the same plan that writes this entity is its history.
21
+ const planned = staged?.entities.get(id);
22
+ if (planned !== undefined)
23
+ return planned.type === "req" ? planned : undefined;
24
+ return (await lookup.entity()) ?? undefined;
25
+ }
26
+ // implements REQ-kibi-upsert-preserves-requirement-semantics
27
+ /**
28
+ * kb_upsert on an existing requirement that changes neither its prose nor its
29
+ * proposition ledger keeps the stored ledger. A payload that adds a
30
+ * relationship (for example `specified_by`) and carries no `semantic_*` or
31
+ * `logic_claims` field, and no `title` or `text_ref` that differs from the
32
+ * stored value, is merged over the stored semantic contract (and the stored
33
+ * `text_ref` it omits) before validation. The
34
+ * proposition-complete check then runs against the merged requirement, which
35
+ * is also what the write records. A payload that supplies any ledger field or
36
+ * changes the prose is validated exactly as given.
37
+ */
38
+ export async function withStoredRequirementSemantics(input, prolog, staged, lookup = storedEntityLookup(prolog, input.id, input.type)) {
39
+ if (input.type !== "req")
40
+ return input;
41
+ const properties = input.properties ?? {};
42
+ if (suppliesSemanticContract(properties))
43
+ return input;
44
+ const stored = await storedRequirement(input.id, lookup, staged);
45
+ if (stored === undefined)
46
+ return input;
47
+ if (PROSE_FIELDS.some((field) => properties[field] !== undefined && properties[field] !== stored[field]))
48
+ return input;
49
+ const preserved = {};
50
+ for (const field of [
51
+ ...SEMANTIC_CONTRACT_FIELDS,
52
+ ...PRESERVED_PROSE_FIELDS,
53
+ ]) {
54
+ if (stored[field] !== undefined)
55
+ preserved[field] = stored[field];
56
+ }
57
+ // Only a recorded ledger is kept. A stored `semantic_text` alone (derived
58
+ // from the body at sync) is not one, and merging it would turn a payload
59
+ // that is valid as sent into one the ingestion boundary rejects.
60
+ if (stored.semantic_inventory_version === undefined ||
61
+ !Array.isArray(stored.semantic_inventory))
62
+ return input;
63
+ return { ...input, properties: { ...preserved, ...properties } };
64
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAMjF,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAmBH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,IAAI,CAiBN;AAED;;;;GAIG;AAEH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,IAAI,CAMN;AAgBD,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CAqCR;AAED,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,CAoBR;AAsBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAwFD,uFAAuF;AAEvF,eAAO,MAAM,uBAAuB,uGAM1B,CAAC;AA8BX,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiCzC;AAoRD,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB;IAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA6C7C;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4DpC;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiEpC;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GACnB,MAAM,CAsBR;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC5C,OAAO,CAAC,EAAE,gBAAgB,EAC1B,sBAAsB,CAAC,EAAE,MAAM,GAC9B,OAAO,CAAC;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,GAAG,IAAI,CAAC,CAoHR;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC3C,MAAM,CAOR"}
1
+ {"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAMjF,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,YAAY,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC,CAAC;AAmBH,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,IAAI,CAiBN;AAED;;;;GAIG;AAEH,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,IAAI,CAMN;AAgBD,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CAqCR;AAED,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,CAoBR;AAsBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAYpB;AAED,wBAAgB,yBAAyB,CACvC,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GACX,OAAO,CAET;AAkGD,uFAAuF;AAEvF,eAAO,MAAM,uBAAuB,uGAM1B,CAAC;AA8BX,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,OAAO,GACZ,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAiCzC;AAoRD,wBAAgB,oBAAoB,CAClC,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,GACtB;IAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CA6C7C;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAChD,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA4DpC;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,QAAQ,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAiEpC;AAED;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GACnB,MAAM,CAsBR;AAED,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC5C,OAAO,CAAC,EAAE,gBAAgB,EAC1B,sBAAsB,CAAC,EAAE,MAAM,GAC9B,OAAO,CAAC;IACT,OAAO,EAAE,kBAAkB,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B,GAAG,IAAI,CAAC,CAoHR;AAED,uEAAuE;AACvE,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAC3C,MAAM,CAOR"}
@@ -6,6 +6,7 @@ import { isDeepStrictEqual } from "node:util";
6
6
  import { dump as dumpYaml, load as loadYaml } from "js-yaml";
7
7
  import { parseDocument, stringify } from "yaml";
8
8
  import { OperationError } from "../../cli-errors.js";
9
+ import { requirementSemanticText } from "../../extractors/markdown.js";
9
10
  import { canonicalFilesystemPath } from "../../utils/canonical-path.js";
10
11
  import { CANONICAL_ENTITY_PATHS, isDerivedKbPath, } from "../../utils/kb-paths.js";
11
12
  function digest(value) {
@@ -192,6 +193,15 @@ function renderEntityDocument(input, entity, existingContent) {
192
193
  frontmatter.id = entity.id;
193
194
  frontmatter.title ??= entity.id;
194
195
  frontmatter.type = entity.type;
196
+ // The checked meaning is always stated, never derived from a body that now
197
+ // also carries context sections.
198
+ if (entity.type === "req" &&
199
+ (typeof frontmatter.semantic_text !== "string" ||
200
+ frontmatter.semantic_text.trim() === "")) {
201
+ const derived = requirementSemanticText(body);
202
+ if (derived !== "")
203
+ frontmatter.semantic_text = derived;
204
+ }
195
205
  return `---\n${dumpYaml(frontmatter, { noRefs: true, lineWidth: -1, sortKeys: false })}---${body.startsWith("\n") ? "" : "\n"}${body}`;
196
206
  }
197
207
  /** Generated coordinate fields belong exclusively to `.kb/symbol-coordinates.yaml`. */
@@ -0,0 +1,20 @@
1
+ import type { PrologPort } from "../../public/operations/runtime-types.js";
2
+ /**
3
+ * The stored entity an upsert replaces: `undefined` when no entity has the
4
+ * id, `null` when one exists but cannot be read.
5
+ */
6
+ export type StoredEntity = Readonly<Record<string, unknown>> | null | undefined;
7
+ export type StoredEntityLookup = {
8
+ /** Whether an entity with the id is stored. */
9
+ readonly exists: () => Promise<boolean>;
10
+ /** The stored entity, loaded only when one exists. */
11
+ readonly entity: () => Promise<StoredEntity>;
12
+ };
13
+ /**
14
+ * One read of the entity an upsert replaces, shared by every step of the
15
+ * upsert that needs it (the requirement ledger merge, the stored
16
+ * relationships and the stored origin), so the store is probed and loaded at
17
+ * most once per upsert.
18
+ */
19
+ export declare function storedEntityLookup(prolog: Pick<PrologPort, "query">, id: string, type: string): StoredEntityLookup;
20
+ //# sourceMappingURL=stored-entity.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,51 @@
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 { escapeAtom } from "../../prolog/codec.js";
19
+ import { loadEntities } from "../../public/operations/discovery-entities.js";
20
+ // implements REQ-kibi-upsert-preserves-requirement-semantics, REQ-kibi-entity-origin
21
+ /**
22
+ * One read of the entity an upsert replaces, shared by every step of the
23
+ * upsert that needs it (the requirement ledger merge, the stored
24
+ * relationships and the stored origin), so the store is probed and loaded at
25
+ * most once per upsert.
26
+ */
27
+ export function storedEntityLookup(prolog, id, type) {
28
+ let probe;
29
+ let read;
30
+ const exists = () => {
31
+ probe ??= prolog
32
+ .query(`once(kb_entity('${escapeAtom(id)}', _, _))`)
33
+ .then((result) => result.success);
34
+ return probe;
35
+ };
36
+ const entity = () => {
37
+ read ??= (async () => {
38
+ if (!(await exists()))
39
+ return undefined;
40
+ try {
41
+ const [existing] = await loadEntities(prolog, { id, type });
42
+ return existing ?? null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ })();
48
+ return read;
49
+ };
50
+ return { exists, entity };
51
+ }
@@ -1,10 +1,11 @@
1
1
  import type { OperationContext } from "../../public/operations/runtime-types.js";
2
2
  import type { OperationResult } from "../../public/operations/types.js";
3
3
  import { analyzeSemanticAdvisorInput } from "../semantic-advisor/analyze-prose.js";
4
+ import { type StoredEntityLookup } from "./stored-entity.js";
4
5
  import type { RelationshipInput, StagedUpsertState, UpsertInput, UpsertPayload, ValidatedUpsert } from "./types.js";
5
6
  export declare function assertRelationshipShardContained(workspaceRoot: string, shardPath: string): void;
6
7
  export declare function validateAppendOnlyProofReceipts(entity: Readonly<Record<string, unknown>>, context: OperationContext, staged?: StagedUpsertState): Promise<void>;
7
- export declare function effectiveRelationships(input: UpsertInput, entity: Readonly<Record<string, unknown>>, relationships: readonly RelationshipInput[], context: OperationContext, staged?: StagedUpsertState): Promise<readonly RelationshipInput[]>;
8
+ export declare function effectiveRelationships(input: UpsertInput, entity: Readonly<Record<string, unknown>>, relationships: readonly RelationshipInput[], context: OperationContext, staged?: StagedUpsertState, lookup?: StoredEntityLookup): Promise<readonly RelationshipInput[]>;
8
9
  export type UpsertValidationOptions = Readonly<{
9
10
  /** Skip the append-only receipt check (kibi proof prune only). */
10
11
  readonly allowReceiptsPrune?: boolean;
@@ -29,7 +30,7 @@ export type UpsertValidation = Readonly<{
29
30
  * kb_apply_plan runs it for every plan step before the plan's first write,
30
31
  * passing the earlier steps' writes as `staged`.
31
32
  */
32
- export declare function validateUpsertForCommit(input: UpsertInput, context: OperationContext, options?: UpsertValidationOptions): Promise<UpsertValidation>;
33
+ export declare function validateUpsertForCommit(payload: UpsertInput, context: OperationContext, options?: UpsertValidationOptions): Promise<UpsertValidation>;
33
34
  export type UpsertExecutionOptions = Readonly<{
34
35
  /**
35
36
  * Internal escape hatch for proof-receipt ingest: when provided, the
@@ -1 +1 @@
1
- {"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/upsert.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAUxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AAiCnF,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AA6CpB,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAChB,IAAI,CAkBN;AAmCD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,aAAa,EAAE,SAAS,iBAAiB,EAAE,EAC3C,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA8BvC;AAmCD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC,CAAC,CAAC;AAGH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;CACnE,CAAC,CAAC;AAGH;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,gBAAgB,CAAC,CAkE3B;AAGD,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C;;;;;;OAMG;IAEH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC,CAAC,CAAC;AAyBH,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAggBzC"}
1
+ {"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/upsert.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAUxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AA4BnF,OAAO,EACL,KAAK,kBAAkB,EAExB,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,WAAW,EACX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AA6CpB,wBAAgB,gCAAgC,CAC9C,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GAChB,IAAI,CAkBN;AAmCD,wBAAsB,+BAA+B,CACnD,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,aAAa,EAAE,SAAS,iBAAiB,EAAE,EAC3C,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,iBAAiB,EAC1B,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA8BvC;AAmCD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C,kEAAkE;IAClE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;CACrC,CAAC,CAAC;AAGH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,yEAAyE;IACzE,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACrD,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;CACnE,CAAC,CAAC;AAGH;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,gBAAgB,CAAC,CAgF3B;AAGD,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C;;;;;;OAMG;IAEH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAC;CACxC,CAAC,CAAC;AAyBH,wBAAsB,aAAa,CACjC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAsgBzC"}
@@ -4,7 +4,6 @@ import path from "node:path";
4
4
  import { OperationError } from "../../cli-errors.js";
5
5
  import { readManifestWithCoordinateOverlay } from "../../extractors/manifest.js";
6
6
  import { extractManifestSymbolRecords } from "../../extractors/manifest.js";
7
- import { escapeAtom } from "../../prolog/codec.js";
8
7
  import { loadEntities } from "../../public/operations/discovery-entities.js";
9
8
  import { appendOnlyProofReceiptHistoryErrors } from "../../public/proof-receipt.js";
10
9
  import { appendRelationship, computeShardPath, } from "../../relationships/shards.js";
@@ -13,13 +12,16 @@ import { canonicalFilesystemPath } from "../../utils/canonical-path.js";
13
12
  import { entityIdStyleWarnings } from "../../utils/entity-id-style.js";
14
13
  import { analyzeSemanticAdvisorInput } from "../semantic-advisor/analyze-prose.js";
15
14
  import { assertLogicalGroundingClaimKeys, assertSemanticInventoryBoundary, } from "../semantic-advisor/ingestion-boundary.js";
15
+ import { entityContextWarnings } from "./context-warning.js";
16
16
  import { buildUpsertCommitGoal, formatUpsertError } from "./contradictions.js";
17
17
  import { resolveUpsertOrigin } from "./origin.js";
18
18
  import { planRecoveryNotes, settlePendingPlanApplyJournals, withPlanRecoveryNotes, } from "./plan-apply-journal.js";
19
19
  import { assertPredicateArgumentVocabulary } from "./predicate-vocabulary-guard.js";
20
20
  import { existingRelationships, validateLiveRelationshipTargets, validateRelationshipSources, validateStrictLanePairing, validateSupersedesSourceHistory, } from "./relationships.js";
21
+ import { withStoredRequirementSemantics } from "./requirement-semantics.js";
21
22
  import { MutationRollbackFailureError, MutationSaga } from "./saga.js";
22
23
  import { writePendingSourceReceipt, writeSourceForUpsert, } from "./source-authoring.js";
24
+ import { storedEntityLookup, } from "./stored-entity.js";
23
25
  import { acquireSymbolCompilerLock, releaseSymbolCompilerLock, } from "./symbol-compiler-lock.js";
24
26
  import { validateSymbolGranularity } from "./symbol-granularity.js";
25
27
  import { refreshSymbolCoordinatesForManifest } from "./symbol-refresh.js";
@@ -118,16 +120,16 @@ export async function validateAppendOnlyProofReceipts(entity, context, staged) {
118
120
  throw new Error(`Entity validation failed: ${errors.join("; ")}`);
119
121
  }
120
122
  }
121
- export async function effectiveRelationships(input, entity, relationships, context, staged) {
123
+ export async function effectiveRelationships(input, entity, relationships, context, staged, lookup) {
122
124
  const prolog = requireProlog(context);
123
125
  // Relationships an earlier plan step adds from this entity will exist when
124
126
  // this upsert runs.
125
127
  const plannedOwn = (staged?.relationships ?? []).filter((relationship) => relationship.from === input.id);
126
- const exists = await prolog.query(`once(kb_entity('${escapeAtom(input.id)}', _, _))`);
127
- if (!exists.success && plannedOwn.length === 0)
128
+ const exists = await (lookup ?? storedEntityLookup(prolog, input.id, input.type)).exists();
129
+ if (!exists && plannedOwn.length === 0)
128
130
  return relationships;
129
131
  try {
130
- const current = exists.success
132
+ const current = exists
131
133
  ? (await existingRelationships(prolog, String(entity.id)))
132
134
  // An upsert owns only relationships whose source is the upserted
133
135
  // entity. Incoming relationships must not be copied into its
@@ -186,16 +188,22 @@ function reextractCanonicalSymbolEntity(symbolId, authoredRelativePath, workspac
186
188
  * kb_apply_plan runs it for every plan step before the plan's first write,
187
189
  * passing the earlier steps' writes as `staged`.
188
190
  */
189
- export async function validateUpsertForCommit(input, context, options = {}) {
191
+ export async function validateUpsertForCommit(payload, context, options = {}) {
190
192
  const prolog = requireProlog(context);
191
193
  const staged = options.staged;
192
- const validated = validateUpsertInput(input, context.clock());
194
+ // The payload must be valid on its own before the store is read.
195
+ const checked = validateUpsertInput(payload, context.clock());
196
+ validateRelationshipSources(payload.id, checked.relationships);
197
+ // One read of the entity this upsert replaces, shared by the ledger merge
198
+ // and the stored origin below.
199
+ const lookup = storedEntityLookup(prolog, payload.id, payload.type);
200
+ const input = await withStoredRequirementSemantics(payload, prolog, staged, lookup);
201
+ const validated = input === payload ? checked : validateUpsertInput(input, context.clock());
193
202
  if (options.allowReceiptsPrune !== true) {
194
203
  await validateAppendOnlyProofReceipts(validated.entity, context, staged);
195
204
  }
196
- validateRelationshipSources(input.id, validated.relationships);
197
205
  await validateSymbolGranularity(validated.entity, validated.relationships, context);
198
- const relationships = await effectiveRelationships(input, validated.entity, validated.relationships, context, staged);
206
+ const relationships = await effectiveRelationships(input, validated.entity, validated.relationships, context, staged, lookup);
199
207
  await validateStrictLanePairing(prolog, validated.relationships, staged);
200
208
  await validateLiveRelationshipTargets(prolog, validated.entity, validated.relationships, staged);
201
209
  await validateSupersedesSourceHistory(prolog, validated.entity, validated.relationships, context.workspaceRoot, undefined, staged);
@@ -207,7 +215,7 @@ export async function validateUpsertForCommit(input, context, options = {}) {
207
215
  await assertPredicateArgumentVocabulary(prolog, validated.entity, staged);
208
216
  // The origin the write records is added last: none of the checks above
209
217
  // reads it, and a rejected payload never pays for the stored-origin read.
210
- const recorded = await resolveUpsertOrigin(input, prolog, context.clock(), staged);
218
+ const recorded = await resolveUpsertOrigin(input, prolog, context.clock(), staged, lookup);
211
219
  return {
212
220
  validated: recorded === input
213
221
  ? validated
@@ -516,6 +524,7 @@ export async function executeUpsert(input, context, options = {}) {
516
524
  ...semantic.warnings,
517
525
  ...coverage,
518
526
  ...idStyleWarnings,
527
+ ...entityContextWarnings(input, commitEntity ?? validated.entity, context.workspaceRoot, sourceWrite?.receipt.path),
519
528
  ...shardWarnings,
520
529
  ],
521
530
  semanticAdvisor: semantic.receipt,
@@ -1 +1 @@
1
- {"version":3,"file":"validate-upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/validate-upsert.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAMxE,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA+CrE;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CAwDjD"}
1
+ {"version":3,"file":"validate-upsert.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/validate-upsert.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAEjB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAOxE,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA+CrE;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAC,CA4DjD"}
@@ -3,6 +3,7 @@ import { escapeAtom } from "../../prolog/codec.js";
3
3
  import { entityIdStyleWarnings } from "../../utils/entity-id-style.js";
4
4
  import { analyzeSemanticAdvisorInput } from "../semantic-advisor/analyze-prose.js";
5
5
  import { assertSemanticInventoryBoundary } from "../semantic-advisor/ingestion-boundary.js";
6
+ import { entityContextWarnings } from "./context-warning.js";
6
7
  import { validateRelationshipSources } from "./relationships.js";
7
8
  import { validateSymbolGranularity } from "./symbol-granularity.js";
8
9
  import { validateAppendOnlyProofReceipts, validateUpsertForCommit, } from "./upsert.js";
@@ -57,7 +58,11 @@ export async function executeValidateUpsert(input, context) {
57
58
  const payload = {
58
59
  valid: true,
59
60
  errors: [],
60
- warnings: [...semantic.warnings, ...idStyleWarnings],
61
+ warnings: [
62
+ ...semantic.warnings,
63
+ ...idStyleWarnings,
64
+ ...entityContextWarnings(input, entity, context.workspaceRoot),
65
+ ],
61
66
  semanticAdvisor: semantic.receipt,
62
67
  normalizedPreview: entity,
63
68
  };
@@ -14,6 +14,15 @@ export type CompileIntentArgs = Readonly<{
14
14
  scenarioDrafts?: readonly ScenarioDraft[];
15
15
  testDrafts?: readonly TestDraft[];
16
16
  proposalDecisions?: readonly ProposalDecision[];
17
+ /**
18
+ * Why the requirement exists, who asked and what does not fit its checked
19
+ * meaning. Required when mode is create; rendered as `## Context`.
20
+ */
21
+ context?: string;
22
+ /** Verbatim text the intent came from; rendered as a blockquote in `## Source`. */
23
+ sourceExcerpt?: string;
24
+ /** Locator of that text (ticket, URL, path); rendered in `## Source`. */
25
+ sourceReference?: string;
17
26
  }>;
18
27
  export type ScenarioDraft = Readonly<{
19
28
  id?: string;