kibi-cli 2.10.0 → 2.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/operations/bootstrap/candidates.d.ts +2 -1
- package/dist/operations/bootstrap/candidates.d.ts.map +1 -1
- package/dist/operations/bootstrap/candidates.js +2 -2
- package/dist/operations/bootstrap/generate.d.ts.map +1 -1
- package/dist/operations/bootstrap/generate.js +6 -2
- package/dist/operations/bootstrap/intent-claims.d.ts +2 -1
- package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
- package/dist/operations/bootstrap/intent-claims.js +6 -4
- package/dist/operations/bootstrap/markdown-candidates.d.ts +2 -1
- package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
- package/dist/operations/bootstrap/markdown-candidates.js +7 -5
- package/dist/operations/bootstrap/requirement-claims.d.ts +45 -2
- package/dist/operations/bootstrap/requirement-claims.d.ts.map +1 -1
- package/dist/operations/bootstrap/requirement-claims.js +224 -19
- package/dist/operations/modeling/predicate-applyplan.d.ts +7 -0
- package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applyplan.js +39 -12
- package/dist/operations/modeling/predicate-binding-hints.d.ts +11 -0
- package/dist/operations/modeling/predicate-binding-hints.d.ts.map +1 -0
- package/dist/operations/modeling/predicate-binding-hints.js +91 -0
- package/dist/operations/modeling/predicate-bindings.d.ts +24 -1
- package/dist/operations/modeling/predicate-bindings.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-bindings.js +113 -3
- package/dist/operations/modeling/predicate-types.d.ts +18 -0
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
- package/dist/operations/modeling/suggest-predicates.js +74 -14
- package/dist/operations/semantic-advisor/observation-plan.d.ts +7 -0
- package/dist/operations/semantic-advisor/observation-plan.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/observation-plan.js +13 -14
- package/dist/public/operations/contracts.d.ts.map +1 -1
- package/dist/public/operations/contracts.js +4 -0
- package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/dist/public/skills/kibi-usage/SKILL.md +15 -7
- package/dist/public/skills/kibi-usage/resources/workflows.md +2 -2
- package/package.json +1 -1
- package/src/public/operations/contracts.ts +5 -0
- package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
- package/src/public/skills/kibi-usage/SKILL.md +15 -7
- package/src/public/skills/kibi-usage/resources/workflows.md +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CandidateBuildResult } from "./markdown-candidates.js";
|
|
2
|
+
import type { SubjectKeyRegistry } from "./requirement-claims.js";
|
|
2
3
|
import type { BootstrapEvidence } from "./types.js";
|
|
3
|
-
export declare function buildBootstrapCandidates(evidence: readonly BootstrapEvidence[], existingIds: ReadonlySet<string>, minConfidence: number, includeGenericMarkdown: boolean): CandidateBuildResult;
|
|
4
|
+
export declare function buildBootstrapCandidates(evidence: readonly BootstrapEvidence[], existingIds: ReadonlySet<string>, minConfidence: number, includeGenericMarkdown: boolean, subjectKeys?: SubjectKeyRegistry): CandidateBuildResult;
|
|
4
5
|
//# sourceMappingURL=candidates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"candidates.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/candidates.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAuHpB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,EACtC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,EACrB,sBAAsB,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"candidates.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/candidates.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAuHpB,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,EACtC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,EACrB,sBAAsB,EAAE,OAAO,EAC/B,WAAW,CAAC,EAAE,kBAAkB,GAC/B,oBAAoB,CAiEtB"}
|
|
@@ -90,7 +90,7 @@ function providerCandidate(item, existingIds, minConfidence) {
|
|
|
90
90
|
}
|
|
91
91
|
// implements REQ-KIBI-BOOTSTRAP-PLAN, REQ-kibi-operation-interface-parity
|
|
92
92
|
// implements REQ-KIBI-BOOTSTRAP-PLAN
|
|
93
|
-
export function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown) {
|
|
93
|
+
export function buildBootstrapCandidates(evidence, existingIds, minConfidence, includeGenericMarkdown, subjectKeys) {
|
|
94
94
|
const candidates = [];
|
|
95
95
|
const sourceOnlySignals = [];
|
|
96
96
|
const diagnostics = [];
|
|
@@ -114,7 +114,7 @@ export function buildBootstrapCandidates(evidence, existingIds, minConfidence, i
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
if (item.kind === "generic_markdown" && includeGenericMarkdown) {
|
|
117
|
-
const built = markdownCandidates(item, existingIds, minConfidence);
|
|
117
|
+
const built = markdownCandidates(item, existingIds, minConfidence, subjectKeys);
|
|
118
118
|
candidates.push(...built.candidates);
|
|
119
119
|
sourceOnlySignals.push(...built.sourceOnlySignals);
|
|
120
120
|
diagnostics.push(...built.diagnostics);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/generate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/generate.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAWjF,OAAO,KAAK,EACV,SAAS,EACT,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAGpB,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,SAAS,gBAAgB,EAAE,EACpC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACzC,SAAS,gBAAgB,EAAE,CAI7B;AA+BD,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM,GACd;IACD,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CA6IA;AA+JD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CA8G9B"}
|
|
@@ -8,6 +8,7 @@ import { buildBootstrapCandidates } from "./candidates.js";
|
|
|
8
8
|
import { discoverBootstrap } from "./discovery.js";
|
|
9
9
|
import { buildIntentClaimCandidates } from "./intent-claims.js";
|
|
10
10
|
import { normalizeBootstrapContext, presentBootstrap } from "./presentation.js";
|
|
11
|
+
import { SubjectKeyRegistry } from "./requirement-claims.js";
|
|
11
12
|
import { validateBootstrapPayload } from "./validation.js";
|
|
12
13
|
export function filterSourceOnlySignals(signals, entityTypes) {
|
|
13
14
|
return entityTypes && entityTypes.length > 0
|
|
@@ -316,8 +317,11 @@ export async function executePlanBootstrap(args, context) {
|
|
|
316
317
|
const bound = await expectedSnapshots(context, discovery.evidence
|
|
317
318
|
.map((evidence) => evidence.relativePath)
|
|
318
319
|
.filter((value) => Boolean(value)));
|
|
320
|
+
// One registry per plan keeps subject keys distinct across repository
|
|
321
|
+
// documents and declared intent claims.
|
|
322
|
+
const subjectKeys = new SubjectKeyRegistry();
|
|
319
323
|
const built = discovery.activation.allowCandidateGeneration
|
|
320
|
-
? buildBootstrapCandidates(discovery.evidence, new Set(), minConfidence, includeGenericMarkdown)
|
|
324
|
+
? buildBootstrapCandidates(discovery.evidence, new Set(), minConfidence, includeGenericMarkdown, subjectKeys)
|
|
321
325
|
: {
|
|
322
326
|
candidates: [],
|
|
323
327
|
sourceOnlySignals: [],
|
|
@@ -327,7 +331,7 @@ export async function executePlanBootstrap(args, context) {
|
|
|
327
331
|
// Intent harvested from declared knowledge sources joins the repository
|
|
328
332
|
// evidence under the same activation policy, filters, and selection.
|
|
329
333
|
const claimed = discovery.activation.allowCandidateGeneration
|
|
330
|
-
? buildIntentClaimCandidates(declared, existingIds, minConfidence)
|
|
334
|
+
? buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys)
|
|
331
335
|
: {
|
|
332
336
|
candidates: [],
|
|
333
337
|
sourceOnlySignals: [],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SubjectKeyRegistry } from "./requirement-claims.js";
|
|
1
2
|
import type { BootstrapDeclaredContext, Candidate, SourceOnlySignal } from "./types.js";
|
|
2
3
|
export type IntentClaimBuildResult = {
|
|
3
4
|
readonly candidates: readonly Candidate[];
|
|
@@ -13,5 +14,5 @@ export type IntentClaimBuildResult = {
|
|
|
13
14
|
* strict modeler cannot ground stays an explicit authoring follow-up instead
|
|
14
15
|
* of becoming unverifiable knowledge.
|
|
15
16
|
*/
|
|
16
|
-
export declare function buildIntentClaimCandidates(declared: BootstrapDeclaredContext, existingIds: ReadonlySet<string>, minConfidence: number): IntentClaimBuildResult;
|
|
17
|
+
export declare function buildIntentClaimCandidates(declared: BootstrapDeclaredContext, existingIds: ReadonlySet<string>, minConfidence: number, subjectKeys?: SubjectKeyRegistry): IntentClaimBuildResult;
|
|
17
18
|
//# sourceMappingURL=intent-claims.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intent-claims.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/intent-claims.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"intent-claims.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/intent-claims.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,kBAAkB,EAGxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,wBAAwB,EAGxB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAQpB,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;IAClE,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC,CAAC;AAyOF;;;;;;;GAOG;AAEH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,wBAAwB,EAClC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,kBAAkB,GAC/B,sBAAsB,CAyKxB"}
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { renderRequirementBody } from "../../entity-body-context.js";
|
|
3
3
|
import { buildStrictWriteSet } from "../../utils/strict-modeling.js";
|
|
4
4
|
import { confidenceBand, strictPlan, upsert } from "./candidate-helpers.js";
|
|
5
|
-
import { claimFor,
|
|
5
|
+
import { claimFor, planSubjectKey, } from "./requirement-claims.js";
|
|
6
6
|
/** How much a claim is trusted follows the authority the human assigned. */
|
|
7
7
|
const AUTHORITY_CONFIDENCE = {
|
|
8
8
|
authoritative: 0.9,
|
|
@@ -199,7 +199,7 @@ function conflictCandidates(declared, sources, existingIds, minConfidence) {
|
|
|
199
199
|
* of becoming unverifiable knowledge.
|
|
200
200
|
*/
|
|
201
201
|
// implements REQ-kibi-bootstrap-knowledge-sources, REQ-bootstrap-claim-kinds-and-conflicts
|
|
202
|
-
export function buildIntentClaimCandidates(declared, existingIds, minConfidence) {
|
|
202
|
+
export function buildIntentClaimCandidates(declared, existingIds, minConfidence, subjectKeys) {
|
|
203
203
|
const sources = new Map((declared.knowledgeSources ?? []).map((source) => [source.id, source]));
|
|
204
204
|
const candidates = [];
|
|
205
205
|
const sourceOnlySignals = [];
|
|
@@ -260,9 +260,11 @@ export function buildIntentClaimCandidates(declared, existingIds, minConfidence)
|
|
|
260
260
|
let writeSet = null;
|
|
261
261
|
try {
|
|
262
262
|
const modeled = claimFor(claim.statement, `knowledge-source:${source.id}`, confidence, provenance);
|
|
263
|
-
const
|
|
264
|
-
?
|
|
263
|
+
const planned = modeled
|
|
264
|
+
? planSubjectKey(modeled.subjectKey, modeled.propertyKey, claim.component ?? source.component, subjectKeys, `${source.id}:${claim.reference}`)
|
|
265
265
|
: null;
|
|
266
|
+
const subjectKey = planned?.resolution ?? null;
|
|
267
|
+
diagnostics.push(...(planned?.diagnostics ?? []));
|
|
266
268
|
writeSet =
|
|
267
269
|
modeled && subjectKey?.ok
|
|
268
270
|
? buildStrictWriteSet({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SubjectKeyRegistry } from "./requirement-claims.js";
|
|
1
2
|
import type { BootstrapEvidence, Candidate, SourceOnlySignal } from "./types.js";
|
|
2
3
|
export type CandidateBuildResult = {
|
|
3
4
|
readonly candidates: readonly Candidate[];
|
|
@@ -11,5 +12,5 @@ export type CandidateBuildResult = {
|
|
|
11
12
|
* covers recorder). A root README names no component.
|
|
12
13
|
*/
|
|
13
14
|
export declare function markdownComponentHint(relativePath: string): string | undefined;
|
|
14
|
-
export declare function markdownCandidates(item: BootstrapEvidence, existingIds: ReadonlySet<string>, minConfidence: number): CandidateBuildResult;
|
|
15
|
+
export declare function markdownCandidates(item: BootstrapEvidence, existingIds: ReadonlySet<string>, minConfidence: number, subjectKeys?: SubjectKeyRegistry): CandidateBuildResult;
|
|
15
16
|
//# sourceMappingURL=markdown-candidates.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-candidates.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/markdown-candidates.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"markdown-candidates.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/markdown-candidates.ts"],"names":[],"mappings":"AAaA,OAAO,EACL,KAAK,kBAAkB,EAIxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,UAAU,EAAE,SAAS,SAAS,EAAE,CAAC;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACxD,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,UAAU,EAAE,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC;CACnE,CAAC;AAyGF;;;;GAIG;AAEH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS,CAYpB;AA4FD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,iBAAiB,EACvB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,EAChC,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,kBAAkB,GAC/B,oBAAoB,CAoFtB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { buildStrictWriteSet } from "../../utils/strict-modeling.js";
|
|
3
3
|
import { bootstrapProvenance, confidenceBand, provenanceBody, provenanceRequirementBody, slug, strictPlan, upsert, withDocumentBody, } from "./candidate-helpers.js";
|
|
4
|
-
import { claimFor, normalizeClaimStatement,
|
|
4
|
+
import { claimFor, normalizeClaimStatement, planSubjectKey, } from "./requirement-claims.js";
|
|
5
5
|
function headingSignals(heading, sourcePath, textRef, minConfidence) {
|
|
6
6
|
const result = [];
|
|
7
7
|
for (const [kind, pattern, confidence] of [
|
|
@@ -102,7 +102,7 @@ export function markdownComponentHint(relativePath) {
|
|
|
102
102
|
}
|
|
103
103
|
return undefined;
|
|
104
104
|
}
|
|
105
|
-
function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence) {
|
|
105
|
+
function requirementCandidate(item, statement, line, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics) {
|
|
106
106
|
const relativePath = item.relativePath ?? item.label;
|
|
107
107
|
const base = /\bshall\b/i.test(statement)
|
|
108
108
|
? 0.86
|
|
@@ -119,7 +119,8 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
|
|
|
119
119
|
const claim = claimFor(statement, relativePath, confidence, `${relativePath}#L${line}`);
|
|
120
120
|
if (!claim)
|
|
121
121
|
return null;
|
|
122
|
-
const
|
|
122
|
+
const planned = planSubjectKey(claim.subjectKey, claim.propertyKey, markdownComponentHint(relativePath), subjectKeys, `${relativePath}#L${line}`);
|
|
123
|
+
const subjectKey = planned.resolution;
|
|
123
124
|
// Reported by the caller as an extraction failure with this reason,
|
|
124
125
|
// instead of writing a subject key outside component.aspect[.sub].
|
|
125
126
|
if (!subjectKey.ok)
|
|
@@ -130,6 +131,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
|
|
|
130
131
|
});
|
|
131
132
|
if (!writeSet.isStrict)
|
|
132
133
|
return null;
|
|
134
|
+
diagnostics.push(...planned.diagnostics);
|
|
133
135
|
return {
|
|
134
136
|
candidateId: `norm:${writeSet.req.id.toLowerCase()}`,
|
|
135
137
|
entityType: "req",
|
|
@@ -153,7 +155,7 @@ function requirementCandidate(item, statement, line, heading, headingLine, exist
|
|
|
153
155
|
};
|
|
154
156
|
}
|
|
155
157
|
// implements REQ-KIBI-BOOTSTRAP-PLAN
|
|
156
|
-
export function markdownCandidates(item, existingIds, minConfidence) {
|
|
158
|
+
export function markdownCandidates(item, existingIds, minConfidence, subjectKeys) {
|
|
157
159
|
const candidates = [];
|
|
158
160
|
const sourceOnlySignals = [];
|
|
159
161
|
const diagnostics = [];
|
|
@@ -182,7 +184,7 @@ export function markdownCandidates(item, existingIds, minConfidence) {
|
|
|
182
184
|
if (!statement || !/\b(must|shall|should)\b/i.test(statement))
|
|
183
185
|
continue;
|
|
184
186
|
try {
|
|
185
|
-
const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence);
|
|
187
|
+
const candidate = requirementCandidate(item, statement, index + 1, heading, headingLine, existingIds, minConfidence, subjectKeys, diagnostics);
|
|
186
188
|
if (candidate) {
|
|
187
189
|
if (!candidate.applyPlan.some((payload) => payload.type === "req" && existingIds.has(String(payload.id))))
|
|
188
190
|
candidates.push(candidate);
|
|
@@ -4,18 +4,61 @@ export declare function claimFor(statement: string, source: string, confidence:
|
|
|
4
4
|
export type SubjectKeyResolution = {
|
|
5
5
|
readonly ok: true;
|
|
6
6
|
readonly subjectKey: string;
|
|
7
|
+
/** The key before its aspect was shortened, when it was. */
|
|
8
|
+
readonly shortenedFrom?: string;
|
|
7
9
|
} | {
|
|
8
10
|
readonly ok: false;
|
|
9
11
|
readonly reason: string;
|
|
10
12
|
};
|
|
13
|
+
/** The longest aspect a bootstrap key keeps as derived. */
|
|
14
|
+
export declare const MAX_ASPECT_WORDS = 4;
|
|
15
|
+
export declare const MAX_ASPECT_CHARS = 40;
|
|
16
|
+
/**
|
|
17
|
+
* A short noun-phrase aspect for a long one, deterministically: the words
|
|
18
|
+
* before the first clause boundary (when, while, for, and, ...), without
|
|
19
|
+
* leading or trailing stop words; if that is still longer than four words or
|
|
20
|
+
* forty characters, its content words (no stop words or -ly adverbs); if
|
|
21
|
+
* those are still too long, the first content word and the last two, where a
|
|
22
|
+
* noun phrase keeps its head noun. An aspect that already fits is returned
|
|
23
|
+
* unchanged.
|
|
24
|
+
*/
|
|
25
|
+
export declare function shortenSubjectAspect(aspect: string): string;
|
|
11
26
|
/**
|
|
12
27
|
* The bootstrap subject key in the component.aspect[.sub] shape that
|
|
13
28
|
* subject-key-shape checks. A subject that already has that shape is kept.
|
|
14
29
|
* Otherwise the component comes from the declared `component` (claim or
|
|
15
30
|
* knowledge source) and the aspect from the claim's subject; a one-word
|
|
16
31
|
* subject is itself the component and the constrained property becomes the
|
|
17
|
-
* aspect.
|
|
18
|
-
*
|
|
32
|
+
* aspect. An aspect longer than four words or forty characters is shortened
|
|
33
|
+
* to a noun phrase and the original key is returned in `shortenedFrom`. When
|
|
34
|
+
* no component can be named the claim is not resolved, so the caller reports
|
|
35
|
+
* it instead of writing a malformed key.
|
|
19
36
|
*/
|
|
20
37
|
export declare function resolveBootstrapSubjectKey(subject: string, propertyKey: string, component?: string): SubjectKeyResolution;
|
|
38
|
+
/**
|
|
39
|
+
* Keeps the subject keys of one bootstrap plan distinct. Claims about the
|
|
40
|
+
* same subject (the same full key) share a key; when shortening collapses two
|
|
41
|
+
* different subjects onto one key, the later one gets a third segment from
|
|
42
|
+
* its own wording (or a short digest) instead of colliding.
|
|
43
|
+
*/
|
|
44
|
+
export declare class SubjectKeyRegistry {
|
|
45
|
+
private readonly owners;
|
|
46
|
+
private readonly assigned;
|
|
47
|
+
/** The plan-wide key for a resolution, and the key it collided with, if any. */
|
|
48
|
+
assign(resolution: {
|
|
49
|
+
readonly subjectKey: string;
|
|
50
|
+
readonly shortenedFrom?: string;
|
|
51
|
+
}): {
|
|
52
|
+
readonly subjectKey: string;
|
|
53
|
+
readonly collidedWith?: string;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve a claim's subject key for a plan and describe any renaming as a
|
|
58
|
+
* plan diagnostic, so the operator can pick a better name before applying.
|
|
59
|
+
*/
|
|
60
|
+
export declare function planSubjectKey(subject: string, propertyKey: string, component: string | undefined, registry: SubjectKeyRegistry | undefined, location: string): {
|
|
61
|
+
readonly resolution: SubjectKeyResolution;
|
|
62
|
+
readonly diagnostics: string[];
|
|
63
|
+
};
|
|
21
64
|
//# sourceMappingURL=requirement-claims.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"requirement-claims.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/requirement-claims.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"requirement-claims.d.ts","sourceRoot":"","sources":["../../../src/operations/bootstrap/requirement-claims.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,gCAAgC,CAAC;AAGxC,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKjE;AAED,wBAAgB,QAAQ,CACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,aAAa,GAAG,IAAI,CA0DtB;AAED,MAAM,MAAM,oBAAoB,GAC5B;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,GACD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAWpD,2DAA2D;AAC3D,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAsGnC;;;;;;;;GAQG;AAEH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA4B3D;AAmBD;;;;;;;;;;GAUG;AAEH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,oBAAoB,CA2BtB;AAED;;;;;GAKG;AAEH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA6B;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IAEtD,gFAAgF;IAChF,MAAM,CAAC,UAAU,EAAE;QACjB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;KACjC,GAAG;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE;CAyBpE;AAMD;;;GAGG;AAEH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,QAAQ,EAAE,kBAAkB,GAAG,SAAS,EACxC,QAAQ,EAAE,MAAM,GACf;IACD,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;CAChC,CAyBA"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { isConventionalSubjectKey, normalizeSubjectKey, } from "../../utils/strict-modeling.js";
|
|
2
3
|
// implements REQ-KIBI-BOOTSTRAP-PLAN
|
|
3
4
|
export function normalizeClaimStatement(statement) {
|
|
@@ -67,16 +68,164 @@ function snakeSegment(value) {
|
|
|
67
68
|
.replace(/[^a-z0-9]+/g, "_")
|
|
68
69
|
.replace(/^_+|_+$/g, "");
|
|
69
70
|
}
|
|
71
|
+
/** The longest aspect a bootstrap key keeps as derived. */
|
|
72
|
+
export const MAX_ASPECT_WORDS = 4;
|
|
73
|
+
export const MAX_ASPECT_CHARS = 40;
|
|
74
|
+
/** Words that start a dependent clause or a second item: the aspect ends before them. */
|
|
75
|
+
const ASPECT_BOUNDARY_WORDS = new Set([
|
|
76
|
+
"when",
|
|
77
|
+
"whenever",
|
|
78
|
+
"while",
|
|
79
|
+
"if",
|
|
80
|
+
"unless",
|
|
81
|
+
"without",
|
|
82
|
+
"after",
|
|
83
|
+
"before",
|
|
84
|
+
"until",
|
|
85
|
+
"because",
|
|
86
|
+
"once",
|
|
87
|
+
"as",
|
|
88
|
+
"by",
|
|
89
|
+
"via",
|
|
90
|
+
"during",
|
|
91
|
+
"except",
|
|
92
|
+
"so",
|
|
93
|
+
"that",
|
|
94
|
+
"which",
|
|
95
|
+
"who",
|
|
96
|
+
"where",
|
|
97
|
+
"since",
|
|
98
|
+
"for",
|
|
99
|
+
"and",
|
|
100
|
+
"or",
|
|
101
|
+
]);
|
|
102
|
+
/** Words that carry no meaning in a key on their own. */
|
|
103
|
+
const ASPECT_STOP_WORDS = new Set([
|
|
104
|
+
"a",
|
|
105
|
+
"an",
|
|
106
|
+
"the",
|
|
107
|
+
"be",
|
|
108
|
+
"is",
|
|
109
|
+
"are",
|
|
110
|
+
"was",
|
|
111
|
+
"were",
|
|
112
|
+
"been",
|
|
113
|
+
"being",
|
|
114
|
+
"to",
|
|
115
|
+
"of",
|
|
116
|
+
"in",
|
|
117
|
+
"on",
|
|
118
|
+
"at",
|
|
119
|
+
"with",
|
|
120
|
+
"from",
|
|
121
|
+
"into",
|
|
122
|
+
"its",
|
|
123
|
+
"it",
|
|
124
|
+
"their",
|
|
125
|
+
"they",
|
|
126
|
+
"them",
|
|
127
|
+
"this",
|
|
128
|
+
"these",
|
|
129
|
+
"those",
|
|
130
|
+
"can",
|
|
131
|
+
"could",
|
|
132
|
+
"will",
|
|
133
|
+
"would",
|
|
134
|
+
"may",
|
|
135
|
+
"might",
|
|
136
|
+
"must",
|
|
137
|
+
"shall",
|
|
138
|
+
"should",
|
|
139
|
+
"do",
|
|
140
|
+
"does",
|
|
141
|
+
"has",
|
|
142
|
+
"have",
|
|
143
|
+
"had",
|
|
144
|
+
"any",
|
|
145
|
+
"all",
|
|
146
|
+
"each",
|
|
147
|
+
"every",
|
|
148
|
+
"some",
|
|
149
|
+
"only",
|
|
150
|
+
"also",
|
|
151
|
+
"then",
|
|
152
|
+
"than",
|
|
153
|
+
"not",
|
|
154
|
+
"no",
|
|
155
|
+
]);
|
|
156
|
+
function fitsAspect(words) {
|
|
157
|
+
return (words.length > 0 &&
|
|
158
|
+
words.length <= MAX_ASPECT_WORDS &&
|
|
159
|
+
words.join("_").length <= MAX_ASPECT_CHARS);
|
|
160
|
+
}
|
|
161
|
+
function trimStopWords(words) {
|
|
162
|
+
let start = 0;
|
|
163
|
+
let end = words.length;
|
|
164
|
+
while (start < end && ASPECT_STOP_WORDS.has(words[start] ?? ""))
|
|
165
|
+
start += 1;
|
|
166
|
+
while (end > start && ASPECT_STOP_WORDS.has(words[end - 1] ?? ""))
|
|
167
|
+
end -= 1;
|
|
168
|
+
return words.slice(start, end);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* A short noun-phrase aspect for a long one, deterministically: the words
|
|
172
|
+
* before the first clause boundary (when, while, for, and, ...), without
|
|
173
|
+
* leading or trailing stop words; if that is still longer than four words or
|
|
174
|
+
* forty characters, its content words (no stop words or -ly adverbs); if
|
|
175
|
+
* those are still too long, the first content word and the last two, where a
|
|
176
|
+
* noun phrase keeps its head noun. An aspect that already fits is returned
|
|
177
|
+
* unchanged.
|
|
178
|
+
*/
|
|
179
|
+
// implements REQ-bootstrap-claim-name-length
|
|
180
|
+
export function shortenSubjectAspect(aspect) {
|
|
181
|
+
const words = aspect.split("_").filter(Boolean);
|
|
182
|
+
if (fitsAspect(words))
|
|
183
|
+
return aspect;
|
|
184
|
+
const boundary = words.findIndex((word, index) => index > 0 &&
|
|
185
|
+
ASPECT_BOUNDARY_WORDS.has(word) &&
|
|
186
|
+
trimStopWords(words.slice(0, index)).length > 0);
|
|
187
|
+
const head = trimStopWords(boundary < 0 ? words : words.slice(0, boundary));
|
|
188
|
+
if (fitsAspect(head))
|
|
189
|
+
return head.join("_");
|
|
190
|
+
// Content words only: no stop words, no -ly adverbs.
|
|
191
|
+
const content = head.filter((word) => !ASPECT_STOP_WORDS.has(word) && !(word.length > 4 && word.endsWith("ly")));
|
|
192
|
+
if (fitsAspect(content))
|
|
193
|
+
return content.join("_");
|
|
194
|
+
// The leading word (the action or state) and the closing words, where an
|
|
195
|
+
// English noun phrase keeps its head noun.
|
|
196
|
+
const picked = Array.from(new Set([content[0], ...content.slice(-(MAX_ASPECT_WORDS - 2))])).filter((word) => typeof word === "string");
|
|
197
|
+
while (picked.length > 1 && picked.join("_").length > MAX_ASPECT_CHARS)
|
|
198
|
+
picked.splice(1, 1);
|
|
199
|
+
const short = picked.join("_");
|
|
200
|
+
return short.length > 0
|
|
201
|
+
? short.slice(0, MAX_ASPECT_CHARS).replace(/_+$/, "")
|
|
202
|
+
: (words[0] ?? aspect).slice(0, MAX_ASPECT_CHARS);
|
|
203
|
+
}
|
|
204
|
+
function withShortAspect(component, aspect) {
|
|
205
|
+
const full = `${component}.${aspect}`;
|
|
206
|
+
const short = shortenSubjectAspect(aspect);
|
|
207
|
+
const candidate = `${component}.${short}`;
|
|
208
|
+
if (!isConventionalSubjectKey(candidate))
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
reason: `subject key "${candidate}" does not follow component.aspect[.sub]`,
|
|
212
|
+
};
|
|
213
|
+
return short === aspect
|
|
214
|
+
? { ok: true, subjectKey: candidate }
|
|
215
|
+
: { ok: true, subjectKey: candidate, shortenedFrom: full };
|
|
216
|
+
}
|
|
70
217
|
/**
|
|
71
218
|
* The bootstrap subject key in the component.aspect[.sub] shape that
|
|
72
219
|
* subject-key-shape checks. A subject that already has that shape is kept.
|
|
73
220
|
* Otherwise the component comes from the declared `component` (claim or
|
|
74
221
|
* knowledge source) and the aspect from the claim's subject; a one-word
|
|
75
222
|
* subject is itself the component and the constrained property becomes the
|
|
76
|
-
* aspect.
|
|
77
|
-
*
|
|
223
|
+
* aspect. An aspect longer than four words or forty characters is shortened
|
|
224
|
+
* to a noun phrase and the original key is returned in `shortenedFrom`. When
|
|
225
|
+
* no component can be named the claim is not resolved, so the caller reports
|
|
226
|
+
* it instead of writing a malformed key.
|
|
78
227
|
*/
|
|
79
|
-
// implements REQ-bootstrap-subject-key-shape
|
|
228
|
+
// implements REQ-bootstrap-subject-key-shape, REQ-bootstrap-claim-name-length
|
|
80
229
|
export function resolveBootstrapSubjectKey(subject, propertyKey, component) {
|
|
81
230
|
const stripped = subject.trim().replace(/^(?:the|a|an)\s+/i, "");
|
|
82
231
|
let key;
|
|
@@ -91,28 +240,84 @@ export function resolveBootstrapSubjectKey(subject, propertyKey, component) {
|
|
|
91
240
|
const flat = key.replace(/\./g, "_");
|
|
92
241
|
const comp = component === undefined ? "" : snakeSegment(component);
|
|
93
242
|
const property = snakeSegment(propertyKey);
|
|
94
|
-
let candidate;
|
|
95
243
|
if (comp) {
|
|
96
244
|
const aspect = flat === comp
|
|
97
245
|
? property
|
|
98
246
|
: flat.startsWith(`${comp}_`)
|
|
99
247
|
? flat.slice(comp.length + 1)
|
|
100
248
|
: flat;
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
else if (/^[a-z][a-z0-9]*$/.test(flat) && property) {
|
|
104
|
-
candidate = `${flat}.${property}`;
|
|
249
|
+
return withShortAspect(comp, aspect);
|
|
105
250
|
}
|
|
106
|
-
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
251
|
+
if (/^[a-z][a-z0-9]*$/.test(flat) && property)
|
|
252
|
+
return withShortAspect(flat, property);
|
|
253
|
+
return {
|
|
254
|
+
ok: false,
|
|
255
|
+
reason: `subject key "${flat}" names no component, so it would violate component.aspect[.sub]`,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Keeps the subject keys of one bootstrap plan distinct. Claims about the
|
|
260
|
+
* same subject (the same full key) share a key; when shortening collapses two
|
|
261
|
+
* different subjects onto one key, the later one gets a third segment from
|
|
262
|
+
* its own wording (or a short digest) instead of colliding.
|
|
263
|
+
*/
|
|
264
|
+
// implements REQ-bootstrap-claim-name-length
|
|
265
|
+
export class SubjectKeyRegistry {
|
|
266
|
+
owners = new Map();
|
|
267
|
+
assigned = new Map();
|
|
268
|
+
/** The plan-wide key for a resolution, and the key it collided with, if any. */
|
|
269
|
+
assign(resolution) {
|
|
270
|
+
const identity = resolution.shortenedFrom ?? resolution.subjectKey;
|
|
271
|
+
const known = this.assigned.get(identity);
|
|
272
|
+
if (known !== undefined)
|
|
273
|
+
return { subjectKey: known };
|
|
274
|
+
const owner = this.owners.get(resolution.subjectKey);
|
|
275
|
+
if (owner === undefined || owner === identity) {
|
|
276
|
+
this.owners.set(resolution.subjectKey, identity);
|
|
277
|
+
this.assigned.set(identity, resolution.subjectKey);
|
|
278
|
+
return { subjectKey: resolution.subjectKey };
|
|
279
|
+
}
|
|
280
|
+
const used = new Set(resolution.subjectKey.split(/[._]/));
|
|
281
|
+
const fullWords = identity.split(".").slice(1).join("_").split("_");
|
|
282
|
+
const qualifier = fullWords.find((word) => /^[a-z][a-z0-9]*$/.test(word) &&
|
|
283
|
+
!used.has(word) &&
|
|
284
|
+
!ASPECT_STOP_WORDS.has(word) &&
|
|
285
|
+
!ASPECT_BOUNDARY_WORDS.has(word) &&
|
|
286
|
+
!this.owners.has(`${resolution.subjectKey}.${word}`));
|
|
287
|
+
const subjectKey = `${resolution.subjectKey}.${qualifier ?? `k${digest(identity)}`}`;
|
|
288
|
+
this.owners.set(subjectKey, identity);
|
|
289
|
+
this.assigned.set(identity, subjectKey);
|
|
290
|
+
return { subjectKey, collidedWith: resolution.subjectKey };
|
|
111
291
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
292
|
+
}
|
|
293
|
+
function digest(value) {
|
|
294
|
+
return createHash("sha256").update(value).digest("hex").slice(0, 6);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Resolve a claim's subject key for a plan and describe any renaming as a
|
|
298
|
+
* plan diagnostic, so the operator can pick a better name before applying.
|
|
299
|
+
*/
|
|
300
|
+
// implements REQ-bootstrap-claim-name-length
|
|
301
|
+
export function planSubjectKey(subject, propertyKey, component, registry, location) {
|
|
302
|
+
const resolved = resolveBootstrapSubjectKey(subject, propertyKey, component);
|
|
303
|
+
if (!resolved.ok)
|
|
304
|
+
return { resolution: resolved, diagnostics: [] };
|
|
305
|
+
const diagnostics = [];
|
|
306
|
+
const assigned = registry?.assign(resolved) ?? {
|
|
307
|
+
subjectKey: resolved.subjectKey,
|
|
308
|
+
};
|
|
309
|
+
if (resolved.shortenedFrom)
|
|
310
|
+
diagnostics.push(`subject-key-shortened: claim at ${location} would have the subject key ${resolved.shortenedFrom}; its aspect is longer than ${MAX_ASPECT_WORDS} words or ${MAX_ASPECT_CHARS} characters, so the plan uses ${assigned.subjectKey}. Rename the subject after apply if another noun phrase fits better.`);
|
|
311
|
+
if (assigned.collidedWith)
|
|
312
|
+
diagnostics.push(`subject-key-disambiguated: claim at ${location} names a different subject than an earlier claim keyed ${assigned.collidedWith}, so the plan uses ${assigned.subjectKey} instead of reusing that key.`);
|
|
313
|
+
return {
|
|
314
|
+
resolution: {
|
|
315
|
+
ok: true,
|
|
316
|
+
subjectKey: assigned.subjectKey,
|
|
317
|
+
...(resolved.shortenedFrom
|
|
318
|
+
? { shortenedFrom: resolved.shortenedFrom }
|
|
319
|
+
: {}),
|
|
320
|
+
},
|
|
321
|
+
diagnostics,
|
|
322
|
+
};
|
|
118
323
|
}
|
|
@@ -8,6 +8,13 @@ export declare function buildSuggestion(schema: PredicateSchemaCandidate, text:
|
|
|
8
8
|
}): PredicateSuggestion;
|
|
9
9
|
export declare function buildPredicateApplyPlan(suggestion: PredicateSuggestion, args: SuggestPredicatesArgs): Array<Record<string, unknown>>;
|
|
10
10
|
export declare function buildRelationshipPlan(factId: string | undefined, requirementId: string | undefined, claimText?: string, existingLogicClaims?: readonly string[]): Record<string, unknown> | null;
|
|
11
|
+
/**
|
|
12
|
+
* The ontology-gap observation plan. It is a review note, not a semantic
|
|
13
|
+
* claim: it quotes the claim in claim_text without a claim_key, carries the
|
|
14
|
+
* review tags in `tags` only (a tag is not an entity, so no relationship
|
|
15
|
+
* targets it) and explains itself in `document.body`, so kb_upsert accepts
|
|
16
|
+
* it unchanged.
|
|
17
|
+
*/
|
|
11
18
|
export declare function buildGapApplyPlan(text: string, args: SuggestPredicatesArgs): Array<Record<string, unknown>>;
|
|
12
19
|
/**
|
|
13
20
|
* Build a review-only schema draft when retrieval found no eligible schema.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"predicate-applyplan.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-applyplan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAQ9B,wBAAgB,eAAe,CAC7B,MAAM,EAAE,wBAAwB,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,gBAAgB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EACvD,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,EAChC,WAAW,CAAC,EAAE;IACZ,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACtC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GACA,mBAAmB,
|
|
1
|
+
{"version":3,"file":"predicate-applyplan.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-applyplan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAEV,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,sBAAsB,CAAC;AAQ9B,wBAAgB,eAAe,CAC7B,MAAM,EAAE,wBAAwB,EAChC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,gBAAgB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM,EACvD,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,EAChC,WAAW,CAAC,EAAE;IACZ,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC;IACtC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,GACA,mBAAmB,CAyGrB;AAGD,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,mBAAmB,EAC/B,IAAI,EAAE,qBAAqB,GAC1B,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAqChC;AAGD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,SAAS,CAAC,EAAE,MAAM,EAClB,mBAAmB,GAAE,SAAS,MAAM,EAAO,GAC1C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAuBhC;AAoBD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,qBAAqB,GAC1B,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAwBhC;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,0BAA0B,CAoE5B"}
|
|
@@ -33,7 +33,18 @@ export function buildSuggestion(schema, text, subject, score, argumentBindings =
|
|
|
33
33
|
!(index === 0 &&
|
|
34
34
|
predicateArgs[index] === "launcher" &&
|
|
35
35
|
diagnostics?.explicitSubject !== true &&
|
|
36
|
-
typeof argumentBindings[name] !== "string")
|
|
36
|
+
typeof argumentBindings[name] !== "string"),
|
|
37
|
+
// A subject named through subjectHint is the agent's reviewed
|
|
38
|
+
// subject, even when the schema names its first argument after it.
|
|
39
|
+
index === 0 &&
|
|
40
|
+
diagnostics?.explicitSubject === true &&
|
|
41
|
+
typeof argumentBindings[name] !== "string"
|
|
42
|
+
? { constants: schema.argument_constants?.[name] }
|
|
43
|
+
: {
|
|
44
|
+
argumentName: name,
|
|
45
|
+
argumentNames: schema.argument_names,
|
|
46
|
+
constants: schema.argument_constants?.[name],
|
|
47
|
+
}),
|
|
37
48
|
]));
|
|
38
49
|
const bindingProvenance = aggregateBindingProvenance(Object.values(bindingProvenanceByArgument));
|
|
39
50
|
const unboundArguments = schema.argument_names.filter((name) => undeclaredArguments.has(name) ||
|
|
@@ -136,9 +147,31 @@ export function buildRelationshipPlan(factId, requirementId, claimText, existing
|
|
|
136
147
|
instructions: `Apply the predicate fact ${factId} first, update the requirement with the returned merged logicClaims manifest, then attach this relationship without overwriting other requirement metadata. The relationship target is the planned fact id ${factId}, never a candidates[].id (SUGGEST-...).`,
|
|
137
148
|
};
|
|
138
149
|
}
|
|
139
|
-
|
|
150
|
+
/**
|
|
151
|
+
* The prose an ontology-gap observation carries in `document.body`. Schema 8
|
|
152
|
+
* blocks an observation without body context (entity-context-missing), so the
|
|
153
|
+
* body says why the note exists and quotes the claim it is about.
|
|
154
|
+
*/
|
|
155
|
+
function gapObservationBody(text, args) {
|
|
156
|
+
const origin = [
|
|
157
|
+
args.requirementId ? `requirement ${args.requirementId}` : null,
|
|
158
|
+
args.source ? `source ${args.source}` : null,
|
|
159
|
+
].filter((value) => value !== null);
|
|
160
|
+
return [
|
|
161
|
+
"No available predicate schema fits this claim, so kb_model mode predicates recorded it as an open ontology gap. The observation does not ground the claim; it stays visible for review until a reviewed predicate_schema covers this kind of statement.",
|
|
162
|
+
...(origin.length > 0 ? [`Claim from ${origin.join(", ")}:`] : ["Claim:"]),
|
|
163
|
+
`> ${text.trim().replace(/\n/g, "\n> ")}`,
|
|
164
|
+
].join("\n\n");
|
|
165
|
+
}
|
|
166
|
+
// implements REQ-mcp-suggest-predicates, REQ-model-predicates-plan-roundtrip
|
|
167
|
+
/**
|
|
168
|
+
* The ontology-gap observation plan. It is a review note, not a semantic
|
|
169
|
+
* claim: it quotes the claim in claim_text without a claim_key, carries the
|
|
170
|
+
* review tags in `tags` only (a tag is not an entity, so no relationship
|
|
171
|
+
* targets it) and explains itself in `document.body`, so kb_upsert accepts
|
|
172
|
+
* it unchanged.
|
|
173
|
+
*/
|
|
140
174
|
export function buildGapApplyPlan(text, args) {
|
|
141
|
-
const claimKey = semanticClaimKey(text);
|
|
142
175
|
const factId = hashId("FACT-ONTOLOGY-GAP", [
|
|
143
176
|
args.requirementId ?? "",
|
|
144
177
|
args.source ?? "",
|
|
@@ -152,20 +185,14 @@ export function buildGapApplyPlan(text, args) {
|
|
|
152
185
|
title: "Ontology gap: predicate schema needed",
|
|
153
186
|
status: "active",
|
|
154
187
|
source: args.source ?? "mcp://kibi/suggest-predicates",
|
|
155
|
-
text_ref: args.source,
|
|
188
|
+
...(args.source ? { text_ref: args.source } : {}),
|
|
156
189
|
tags: ["review:ontology-gap", "needs_schema_extension"],
|
|
157
190
|
fact_kind: "observation",
|
|
158
191
|
value_string: text,
|
|
159
|
-
claim_key: claimKey,
|
|
160
192
|
claim_text: text,
|
|
161
193
|
},
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
type: "relates_to",
|
|
165
|
-
from: factId,
|
|
166
|
-
to: "review:ontology-gap",
|
|
167
|
-
},
|
|
168
|
-
],
|
|
194
|
+
document: { body: gapObservationBody(text, args) },
|
|
195
|
+
relationships: [],
|
|
169
196
|
},
|
|
170
197
|
];
|
|
171
198
|
}
|