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.
- package/dist/cli-operation-metadata.d.ts +1 -1
- package/dist/cli-operation-metadata.js +1 -1
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +15 -0
- package/dist/commands/init-helpers.js +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +43 -0
- package/dist/entity-body-context.d.ts +132 -0
- package/dist/entity-body-context.d.ts.map +1 -0
- package/dist/entity-body-context.js +382 -0
- package/dist/extractors/markdown.d.ts +13 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +19 -2
- package/dist/intent-search.d.ts.map +1 -1
- package/dist/intent-search.js +4 -4
- package/dist/operations/bootstrap/candidate-helpers.d.ts +18 -0
- package/dist/operations/bootstrap/candidate-helpers.d.ts.map +1 -1
- package/dist/operations/bootstrap/candidate-helpers.js +35 -0
- package/dist/operations/bootstrap/candidates.d.ts.map +1 -1
- package/dist/operations/bootstrap/candidates.js +4 -2
- package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
- package/dist/operations/bootstrap/intent-claims.js +59 -4
- package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
- package/dist/operations/bootstrap/markdown-candidates.js +7 -3
- package/dist/operations/bootstrap/presentation.js +1 -1
- package/dist/operations/migration/entity-body-context.d.ts +57 -0
- package/dist/operations/migration/entity-body-context.d.ts.map +1 -0
- package/dist/operations/migration/entity-body-context.js +261 -0
- package/dist/operations/migration/polarity-values.d.ts.map +1 -1
- package/dist/operations/migration/polarity-values.js +5 -2
- package/dist/operations/migration/schema6.d.ts +2 -0
- package/dist/operations/migration/schema6.d.ts.map +1 -1
- package/dist/operations/migration/schema6.js +69 -0
- package/dist/operations/modeling/logic-modeling.d.ts.map +1 -1
- package/dist/operations/modeling/logic-modeling.js +3 -1
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +7 -9
- package/dist/operations/mutation/context-warning.d.ts +13 -0
- package/dist/operations/mutation/context-warning.d.ts.map +1 -0
- package/dist/operations/mutation/context-warning.js +59 -0
- package/dist/operations/mutation/origin.d.ts +2 -1
- package/dist/operations/mutation/origin.d.ts.map +1 -1
- package/dist/operations/mutation/origin.js +7 -18
- package/dist/operations/mutation/requirement-semantics.d.ts +16 -0
- package/dist/operations/mutation/requirement-semantics.d.ts.map +1 -0
- package/dist/operations/mutation/requirement-semantics.js +64 -0
- package/dist/operations/mutation/source-authoring.d.ts.map +1 -1
- package/dist/operations/mutation/source-authoring.js +10 -0
- package/dist/operations/mutation/stored-entity.d.ts +20 -0
- package/dist/operations/mutation/stored-entity.d.ts.map +1 -0
- package/dist/operations/mutation/stored-entity.js +51 -0
- package/dist/operations/mutation/upsert.d.ts +3 -2
- package/dist/operations/mutation/upsert.d.ts.map +1 -1
- package/dist/operations/mutation/upsert.js +19 -10
- package/dist/operations/mutation/validate-upsert.d.ts.map +1 -1
- package/dist/operations/mutation/validate-upsert.js +6 -1
- package/dist/operations/planning/compile-intent.d.ts +9 -0
- package/dist/operations/planning/compile-intent.d.ts.map +1 -1
- package/dist/operations/planning/compile-intent.js +49 -2
- package/dist/operations/semantic-advisor/analyze-prose.d.ts +3 -2
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +3 -41
- package/dist/operations/semantic-advisor/ingestion-boundary.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/ingestion-boundary.js +10 -1
- package/dist/operations/semantic-advisor/proposition-role.d.ts +6 -0
- package/dist/operations/semantic-advisor/proposition-role.d.ts.map +1 -0
- package/dist/operations/semantic-advisor/proposition-role.js +76 -0
- package/dist/public/operations/catalog.d.ts +33 -6
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/check-executor.d.ts.map +1 -1
- package/dist/public/operations/check-executor.js +3 -0
- package/dist/public/operations/entity-context.d.ts +51 -0
- package/dist/public/operations/entity-context.d.ts.map +1 -0
- package/dist/public/operations/entity-context.js +235 -0
- package/dist/public/operations/origin-review.d.ts +1 -1
- package/dist/public/operations/origin-review.js +2 -2
- package/dist/public/operations/specs/bootstrap.d.ts +14 -2
- package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
- package/dist/public/operations/specs/bootstrap.js +8 -2
- package/dist/public/operations/specs/check-rules.generated.d.ts +1 -1
- package/dist/public/operations/specs/check-rules.generated.d.ts.map +1 -1
- package/dist/public/operations/specs/check-rules.generated.js +3 -1
- package/dist/public/operations/specs/check.d.ts +1 -1
- package/dist/public/operations/specs/mutation-schemas.d.ts +1 -1
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/mutation-schemas.js +10 -2
- package/dist/public/operations/specs/planning.d.ts +15 -0
- package/dist/public/operations/specs/planning.d.ts.map +1 -1
- package/dist/public/operations/specs/planning.js +15 -0
- package/dist/public/operations/specs/upsert.d.ts +3 -3
- package/dist/public/operations/specs/upsert.js +2 -2
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +7 -1
- package/dist/public/skills/kibi-bootstrap/SKILL.md +3 -3
- package/dist/public/skills/kibi-bootstrap/resources/bootstrap.md +5 -1
- package/dist/public/skills/kibi-usage/SKILL.md +56 -3
- package/dist/public/skills/kibi-usage/resources/source-authoring.md +5 -1
- package/dist/schemas/entity.schema.json +14 -1
- package/dist/search-ranking.d.ts.map +1 -1
- package/dist/search-ranking.js +7 -4
- package/dist/traceability/staged-consistency.d.ts +25 -0
- package/dist/traceability/staged-consistency.d.ts.map +1 -0
- package/dist/traceability/staged-consistency.js +92 -0
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +5 -0
- package/dist/utils/kb-manifest.d.ts +5 -0
- package/dist/utils/kb-manifest.d.ts.map +1 -1
- package/dist/utils/kb-manifest.js +6 -0
- package/dist/utils/rule-registry.generated.d.ts +11 -0
- package/dist/utils/rule-registry.generated.d.ts.map +1 -1
- package/dist/utils/rule-registry.generated.js +13 -0
- package/dist/utils/schema-version.d.ts +1 -1
- package/dist/utils/schema-version.d.ts.map +1 -1
- package/dist/utils/schema-version.js +3 -2
- package/package.json +2 -2
- package/schema/manifest.json +5 -0
- package/schema/validation.pl +14 -2
- package/src/public/operations/check-executor.ts +6 -0
- package/src/public/operations/entity-context.ts +304 -0
- package/src/public/operations/origin-review.ts +2 -2
- package/src/public/operations/specs/bootstrap.ts +9 -2
- package/src/public/operations/specs/check-rules.generated.ts +3 -1
- package/src/public/operations/specs/mutation-schemas.ts +10 -2
- package/src/public/operations/specs/planning.ts +18 -0
- package/src/public/operations/specs/upsert.ts +2 -2
- package/src/public/schemas/entity.ts +7 -1
- package/src/public/skills/kibi-bootstrap/SKILL.md +3 -3
- package/src/public/skills/kibi-bootstrap/resources/bootstrap.md +5 -1
- package/src/public/skills/kibi-usage/SKILL.md +56 -3
- package/src/public/skills/kibi-usage/resources/source-authoring.md +5 -1
- 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:
|
|
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,
|
|
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 {
|
|
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:
|
|
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
|
-
?
|
|
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:
|
|
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":"
|
|
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 {
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"source-authoring.d.ts","sourceRoot":"","sources":["../../../src/operations/mutation/source-authoring.ts"],"names":[],"mappings":"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"}
|
|
@@ -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(
|
|
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;
|
|
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
|
|
127
|
-
if (!exists
|
|
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
|
|
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(
|
|
191
|
+
export async function validateUpsertForCommit(payload, context, options = {}) {
|
|
190
192
|
const prolog = requireProlog(context);
|
|
191
193
|
const staged = options.staged;
|
|
192
|
-
|
|
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;
|
|
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: [
|
|
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;
|