kibi-cli 0.16.1 → 0.17.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/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +1 -1
- package/dist/commands/sync/manifest.d.ts +12 -1
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +22 -5
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +18 -1
- package/dist/extractors/markdown.d.ts +9 -1
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +12 -0
- package/dist/logic/ir.d.ts +112 -0
- package/dist/logic/ir.d.ts.map +1 -0
- package/dist/logic/ir.js +729 -0
- package/dist/operations/modeling/logic-modeling.d.ts +22 -0
- package/dist/operations/modeling/logic-modeling.d.ts.map +1 -0
- package/dist/operations/modeling/logic-modeling.js +111 -0
- package/dist/operations/modeling/model-requirement.d.ts.map +1 -1
- package/dist/operations/modeling/model-requirement.js +56 -0
- package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-applyplan.js +15 -2
- package/dist/operations/modeling/predicate-inference.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-inference.js +17 -2
- package/dist/operations/modeling/predicate-loader.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-loader.js +6 -0
- package/dist/operations/modeling/predicate-ranker.d.ts.map +1 -1
- package/dist/operations/modeling/predicate-ranker.js +40 -2
- package/dist/operations/modeling/predicate-types.d.ts +3 -0
- package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
- package/dist/operations/modeling/requirement-types.d.ts +12 -0
- package/dist/operations/modeling/requirement-types.d.ts.map +1 -1
- package/dist/operations/mutation/relationships.d.ts.map +1 -1
- package/dist/operations/mutation/relationships.js +8 -0
- package/dist/operations/mutation/serialization.d.ts.map +1 -1
- package/dist/operations/mutation/serialization.js +3 -0
- package/dist/operations/mutation/strict-fact.d.ts.map +1 -1
- package/dist/operations/mutation/strict-fact.js +68 -3
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts +2 -2
- package/dist/operations/semantic-advisor/analysis-receipt.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analysis-receipt.js +23 -15
- package/dist/operations/semantic-advisor/analyze-prose.d.ts +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/analyze-prose.js +435 -13
- package/dist/operations/semantic-advisor/observation-plan.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/observation-plan.js +15 -3
- package/dist/operations/semantic-advisor/shared.d.ts +1 -0
- package/dist/operations/semantic-advisor/shared.d.ts.map +1 -1
- package/dist/operations/semantic-advisor/shared.js +1 -1
- package/dist/operations/semantic-advisor/types.d.ts +61 -1
- package/dist/operations/semantic-advisor/types.d.ts.map +1 -1
- package/dist/prolog/codec.d.ts.map +1 -1
- package/dist/prolog/codec.js +11 -1
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +7 -2
- package/dist/public/operations/catalog.d.ts +85 -6
- package/dist/public/operations/catalog.d.ts.map +1 -1
- package/dist/public/operations/index.d.ts +1 -0
- package/dist/public/operations/index.d.ts.map +1 -1
- package/dist/public/operations/index.js +1 -0
- package/dist/public/operations/specs/check.d.ts +2 -2
- package/dist/public/operations/specs/check.d.ts.map +1 -1
- package/dist/public/operations/specs/check.js +4 -1
- package/dist/public/operations/specs/modeling.d.ts +16 -0
- package/dist/public/operations/specs/modeling.d.ts.map +1 -1
- package/dist/public/operations/specs/modeling.js +16 -0
- package/dist/public/operations/specs/mutation-schemas.d.ts +37 -2
- package/dist/public/operations/specs/mutation-schemas.d.ts.map +1 -1
- package/dist/public/operations/specs/mutation-schemas.js +27 -0
- package/dist/public/operations/specs/semantic.d.ts +30 -2
- package/dist/public/operations/specs/semantic.d.ts.map +1 -1
- package/dist/public/operations/specs/semantic.js +34 -2
- package/dist/public/operations/specs/upsert.d.ts +37 -2
- package/dist/public/operations/specs/upsert.d.ts.map +1 -1
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +290 -0
- package/dist/public/schemas/relationship.d.ts.map +1 -1
- package/dist/public/schemas/relationship.js +1 -0
- package/dist/public/skills/kibi-usage/SKILL.md +35 -9
- package/dist/public/skills/kibi-usage/resources/fact-lanes.md +11 -1
- package/dist/public/skills/kibi-usage/resources/logic-ir.md +43 -0
- package/dist/public/skills/kibi-usage/resources/operation-access.md +19 -19
- package/dist/public/skills/kibi-usage/resources/ui-requirements.md +96 -0
- package/dist/public/skills/kibi-usage/resources/workflows.md +21 -6
- package/dist/public/symbol-granularity.d.ts +9 -0
- package/dist/public/symbol-granularity.d.ts.map +1 -1
- package/dist/public/symbol-granularity.js +16 -0
- package/dist/schemas/entity.schema.json +306 -3
- package/dist/traceability/staged-diagnostics.d.ts +1 -1
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -1
- package/dist/traceability/staged-diagnostics.js +17 -10
- package/dist/traceability/staged-impact-contract.js +1 -1
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -1
- package/dist/traceability/staged-symbols-manifest.js +28 -10
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +13 -0
- package/dist/types/relationships.d.ts +1 -1
- package/dist/types/relationships.d.ts.map +1 -1
- package/dist/utils/rule-registry.d.ts.map +1 -1
- package/dist/utils/rule-registry.js +18 -0
- package/dist/utils/schema-version.d.ts +1 -1
- package/dist/utils/schema-version.js +1 -1
- package/package.json +6 -2
- package/src/public/operations/index.ts +1 -0
- package/src/public/operations/specs/check.ts +4 -1
- package/src/public/operations/specs/modeling.ts +20 -0
- package/src/public/operations/specs/mutation-schemas.ts +30 -0
- package/src/public/operations/specs/semantic.ts +41 -2
- package/src/public/schemas/entity.ts +297 -1
- package/src/public/schemas/relationship.ts +1 -0
- package/src/public/skills/kibi-usage/SKILL.md +35 -9
- package/src/public/skills/kibi-usage/resources/fact-lanes.md +11 -1
- package/src/public/skills/kibi-usage/resources/logic-ir.md +43 -0
- package/src/public/skills/kibi-usage/resources/operation-access.md +19 -19
- package/src/public/skills/kibi-usage/resources/ui-requirements.md +96 -0
- package/src/public/skills/kibi-usage/resources/workflows.md +21 -6
- package/src/public/symbol-granularity.ts +25 -0
- package/src/schemas/entity.schema.json +306 -3
- package/src/schemas/relationship.schema.json +1 -0
|
@@ -19,15 +19,19 @@ function summary(readiness, lane) {
|
|
|
19
19
|
return "Requirement prose appears to contain scalar, threshold, or cardinality logic that should be modeled with strict facts.";
|
|
20
20
|
if (lane === "predicate")
|
|
21
21
|
return "Requirement prose appears to contain relational or behavioral logic that should be modeled with ontology predicates.";
|
|
22
|
+
if (lane === "rule")
|
|
23
|
+
return "Requirement prose has a typed interpretation candidate; validate and persist it as a safe kibi.logic.v1 rule.";
|
|
22
24
|
return "Requirement prose appears normative but needs review before it can participate in logic checks.";
|
|
23
25
|
}
|
|
24
26
|
// implements REQ-mcp-semantic-advisor-preflight
|
|
25
|
-
export function buildAdvisorResult(payload, signals, modeled, suggestions, clauses) {
|
|
27
|
+
export function buildAdvisorResult(payload, signals, modeled, suggestions, clauses, propositions, interpretations, shadowAnalysis) {
|
|
26
28
|
const suggestionLane = suggestions.some(({ kind }) => kind === "strict_property")
|
|
27
29
|
? "strict_property"
|
|
28
|
-
: suggestions.some(({ kind }) => kind === "
|
|
29
|
-
? "
|
|
30
|
-
:
|
|
30
|
+
: suggestions.some(({ kind }) => kind === "rule")
|
|
31
|
+
? "rule"
|
|
32
|
+
: suggestions.some(({ kind }) => kind === "predicate")
|
|
33
|
+
? "predicate"
|
|
34
|
+
: null;
|
|
31
35
|
const candidateLane = modeled
|
|
32
36
|
? "none"
|
|
33
37
|
: (suggestionLane ?? chooseLane(signals));
|
|
@@ -42,21 +46,22 @@ export function buildAdvisorResult(payload, signals, modeled, suggestions, claus
|
|
|
42
46
|
? ["kb_model_requirement"]
|
|
43
47
|
: candidateLane === "predicate"
|
|
44
48
|
? ["kb_suggest_predicates"]
|
|
45
|
-
: candidateLane === "
|
|
46
|
-
? ["kb_model_requirement"
|
|
47
|
-
:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
: candidateLane === "rule"
|
|
50
|
+
? ["kb_model_requirement"]
|
|
51
|
+
: candidateLane === "observation_review"
|
|
52
|
+
? ["kb_model_requirement", "kb_suggest_predicates"]
|
|
53
|
+
: [];
|
|
54
|
+
const expectedClaimKeys = propositions
|
|
55
|
+
.filter(({ role }) => !["rationale", "example", "subjective"].includes(role))
|
|
56
|
+
.map(({ claim_key }) => claim_key);
|
|
51
57
|
const rawDeclaredClaimKeys = propertiesOf(payload).logic_claims;
|
|
52
58
|
const declaredClaimKeys = Array.isArray(rawDeclaredClaimKeys)
|
|
53
59
|
? rawDeclaredClaimKeys.filter((value) => typeof value === "string")
|
|
54
60
|
: [];
|
|
55
61
|
const missingClaimKeys = expectedClaimKeys.filter((claimKey) => !declaredClaimKeys.includes(claimKey));
|
|
56
|
-
const unresolvedClaimKeys =
|
|
57
|
-
.filter((
|
|
58
|
-
|
|
59
|
-
.map((suggestion) => suggestion.claim_key);
|
|
62
|
+
const unresolvedClaimKeys = propositions
|
|
63
|
+
.filter(({ status }) => ["ambiguous", "ontology_gap", "missing"].includes(status))
|
|
64
|
+
.map(({ claim_key }) => claim_key);
|
|
60
65
|
const coverageStatus = expectedClaimKeys.length === 0
|
|
61
66
|
? "not_applicable"
|
|
62
67
|
: modeled
|
|
@@ -65,7 +70,7 @@ export function buildAdvisorResult(payload, signals, modeled, suggestions, claus
|
|
|
65
70
|
? "unverified"
|
|
66
71
|
: "partial";
|
|
67
72
|
const receipt = {
|
|
68
|
-
version: "semantic-advisor-
|
|
73
|
+
version: "semantic-advisor-v2",
|
|
69
74
|
payload_hash: payloadHash(payload),
|
|
70
75
|
logic_readiness: readiness,
|
|
71
76
|
candidate_lane: candidateLane,
|
|
@@ -86,6 +91,9 @@ export function buildAdvisorResult(payload, signals, modeled, suggestions, claus
|
|
|
86
91
|
],
|
|
87
92
|
message: "Numeric cardinality prose can mean an exact count, an upper/lower bound, named membership, or an example; model it explicitly before relying on contradiction checks.",
|
|
88
93
|
})),
|
|
94
|
+
propositions,
|
|
95
|
+
interpretations,
|
|
96
|
+
shadow_analysis: shadowAnalysis,
|
|
89
97
|
suggestions,
|
|
90
98
|
clauses: clauses.map((clause) => ({
|
|
91
99
|
...clause,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SemanticAdvisorAnalysisResult, SemanticAdvisorInput } from "./types.js";
|
|
2
|
-
export declare const SEMANTIC_ADVISOR_VERSION = "semantic-advisor-
|
|
2
|
+
export declare const SEMANTIC_ADVISOR_VERSION = "semantic-advisor-v2";
|
|
3
3
|
export { semanticClaimKey } from "./clauses.js";
|
|
4
4
|
export declare function analyzeSemanticAdvisorInput(input: SemanticAdvisorInput): SemanticAdvisorAnalysisResult;
|
|
5
5
|
//# sourceMappingURL=analyze-prose.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze-prose.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/analyze-prose.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"analyze-prose.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/analyze-prose.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EACV,6BAA6B,EAC7B,oBAAoB,EAWrB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,wBAAwB,wBAAwB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AA6zBhD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,oBAAoB,GAC1B,6BAA6B,CAuD/B"}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { utf8Span, validateLogicIr, } from "../../logic/ir.js";
|
|
2
|
+
import { logicRuleFactId } from "../modeling/logic-modeling.js";
|
|
1
3
|
import { buildAdvisorResult } from "./analysis-receipt.js";
|
|
2
|
-
import { extractSemanticClauses } from "./clauses.js";
|
|
4
|
+
import { extractSemanticClauses, semanticClaimKey, } from "./clauses.js";
|
|
3
5
|
import { observationPlan } from "./observation-plan.js";
|
|
4
6
|
import { detectPredicateRules } from "./predicate-rule.js";
|
|
5
7
|
import { CORE_PREDICATE_RULES } from "./predicate-rules-core.js";
|
|
6
8
|
import { POLICY_PREDICATE_RULES } from "./predicate-rules-policy.js";
|
|
7
9
|
import { PRODUCT_TAIL_PREDICATE_RULES } from "./predicate-rules-product-tail.js";
|
|
8
10
|
import { PRODUCT_PREDICATE_RULES } from "./predicate-rules-product.js";
|
|
9
|
-
import { isRecord, propertiesOf, statementOf, stringValue, } from "./shared.js";
|
|
11
|
+
import { isRecord, payloadHash, propertiesOf, relationship, sourceOf, statementOf, stringValue, } from "./shared.js";
|
|
10
12
|
import { detectStrictSuggestion } from "./strict-rules.js";
|
|
11
|
-
export const SEMANTIC_ADVISOR_VERSION = "semantic-advisor-
|
|
13
|
+
export const SEMANTIC_ADVISOR_VERSION = "semantic-advisor-v2";
|
|
12
14
|
export { semanticClaimKey } from "./clauses.js";
|
|
13
15
|
const SIGNAL_PATTERNS = [
|
|
14
16
|
{
|
|
@@ -65,7 +67,7 @@ function logicalGroundingSlots(payload) {
|
|
|
65
67
|
? payload.relationships
|
|
66
68
|
: [];
|
|
67
69
|
return relationships.filter((relationship) => isRecord(relationship) &&
|
|
68
|
-
["requires_property", "requires_predicate"].includes(stringValue(relationship.type))).length;
|
|
70
|
+
["requires_property", "requires_predicate", "requires_rule"].includes(stringValue(relationship.type))).length;
|
|
69
71
|
}
|
|
70
72
|
function isModeled(payload, expectedClaimCount) {
|
|
71
73
|
if (expectedClaimCount === 0)
|
|
@@ -79,7 +81,8 @@ function isModeled(payload, expectedClaimCount) {
|
|
|
79
81
|
.filter(Boolean));
|
|
80
82
|
return (logicalGroundingSlots(payload) >= expectedClaimCount &&
|
|
81
83
|
((types.has("constrains") && types.has("requires_property")) ||
|
|
82
|
-
types.has("requires_predicate")
|
|
84
|
+
types.has("requires_predicate") ||
|
|
85
|
+
types.has("requires_rule")));
|
|
83
86
|
}
|
|
84
87
|
function detectSignals(prose) {
|
|
85
88
|
const seen = new Set();
|
|
@@ -149,7 +152,80 @@ function predicateSuggestion(payload, statement) {
|
|
|
149
152
|
detectPredicateRules(payload, statement, PRODUCT_PREDICATE_RULES) ??
|
|
150
153
|
detectPredicateRules(payload, statement, PRODUCT_TAIL_PREDICATE_RULES));
|
|
151
154
|
}
|
|
152
|
-
function
|
|
155
|
+
function ruleSuggestion(payload, candidate) {
|
|
156
|
+
const validation = validateLogicIr(candidate.ir);
|
|
157
|
+
if (!validation.valid ||
|
|
158
|
+
!validation.normalized ||
|
|
159
|
+
!validation.semanticKey ||
|
|
160
|
+
!validation.renderedProlog)
|
|
161
|
+
return null;
|
|
162
|
+
const reqId = stringValue(payload.id);
|
|
163
|
+
const semanticKey = validation.semanticKey;
|
|
164
|
+
const schemaId = candidate.ir.ruleSchemaId ?? "FACT-RULE-SCHEMA-LOGIC-V1";
|
|
165
|
+
const factId = logicRuleFactId(semanticKey);
|
|
166
|
+
const schemaPlan = candidate.ir.ruleSchemaId
|
|
167
|
+
? []
|
|
168
|
+
: [
|
|
169
|
+
{
|
|
170
|
+
type: "fact",
|
|
171
|
+
id: schemaId,
|
|
172
|
+
properties: {
|
|
173
|
+
title: "kibi.logic.v1 rule schema",
|
|
174
|
+
status: "active",
|
|
175
|
+
source: sourceOf(payload),
|
|
176
|
+
fact_kind: "rule_schema",
|
|
177
|
+
rule_name: "kibi.logic.v1",
|
|
178
|
+
argument_names: ["rule_ir"],
|
|
179
|
+
argument_types: ["logic_ir"],
|
|
180
|
+
aliases: ["conditional rule", "constraint", "policy rule"],
|
|
181
|
+
examples: [validation.renderedProlog],
|
|
182
|
+
},
|
|
183
|
+
relationships: [],
|
|
184
|
+
},
|
|
185
|
+
];
|
|
186
|
+
return {
|
|
187
|
+
kind: "rule",
|
|
188
|
+
claim_key: candidate.claim_key,
|
|
189
|
+
claim_text: candidate.claim_text,
|
|
190
|
+
confidence: candidate.confidence ?? 0.75,
|
|
191
|
+
evidence: candidate.claim_text,
|
|
192
|
+
rationale: "The host supplied a typed kibi.logic.v1 interpretation. Kibi validated its safety, canonicalized it, and will persist it as data-backed rule evidence.",
|
|
193
|
+
suggested_next_tool: "kb_model_requirement",
|
|
194
|
+
rule: validation.normalized,
|
|
195
|
+
semantic_key: semanticKey,
|
|
196
|
+
rendered_prolog: validation.renderedProlog,
|
|
197
|
+
rejected_alternatives: ["raw_prolog", "observation_review"],
|
|
198
|
+
applyPlan: [
|
|
199
|
+
...schemaPlan,
|
|
200
|
+
{
|
|
201
|
+
type: "fact",
|
|
202
|
+
id: factId,
|
|
203
|
+
properties: {
|
|
204
|
+
title: `${candidate.ir.kind} rule ${semanticKey}`,
|
|
205
|
+
status: "active",
|
|
206
|
+
source: sourceOf(payload),
|
|
207
|
+
fact_kind: "rule",
|
|
208
|
+
rule_ir: validation.normalized,
|
|
209
|
+
rule_hash: validation.ruleHash,
|
|
210
|
+
semantic_key: semanticKey,
|
|
211
|
+
rule_schema_id: schemaId,
|
|
212
|
+
rule_name: validation.normalized.ruleSchemaId ?? "kibi.logic.v1",
|
|
213
|
+
canonical_key: semanticKey,
|
|
214
|
+
tags: ["lane:logic", "semantic-advisor-suggestion"],
|
|
215
|
+
},
|
|
216
|
+
relationships: [],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
relationshipPlan: reqId
|
|
220
|
+
? {
|
|
221
|
+
applyAfter: factId,
|
|
222
|
+
requiresExistingReq: reqId,
|
|
223
|
+
relationship: relationship(reqId, factId, "requires_rule"),
|
|
224
|
+
}
|
|
225
|
+
: null,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function modelingSuggestions(payload, modeled, clauses, interpretations) {
|
|
153
229
|
if (!isRequirement(payload) || modeled)
|
|
154
230
|
return [];
|
|
155
231
|
const suggestions = [];
|
|
@@ -169,7 +245,7 @@ function modelingSuggestions(payload, modeled, clauses) {
|
|
|
169
245
|
ambiguitySuggestion(payload, candidate) ??
|
|
170
246
|
detectStrictSuggestion(payload, candidate);
|
|
171
247
|
const suggestion = rawSuggestion
|
|
172
|
-
? withClauseProvenance(rawSuggestion, clause, mergedClaimKeys)
|
|
248
|
+
? withClauseProvenance(rawSuggestion, clause, mergedClaimKeys, statementOf(payload))
|
|
173
249
|
: clause.normative
|
|
174
250
|
? unmatchedClauseSuggestion(payload, clause, mergedClaimKeys)
|
|
175
251
|
: null;
|
|
@@ -181,9 +257,27 @@ function modelingSuggestions(payload, modeled, clauses) {
|
|
|
181
257
|
suggestions.push(suggestion);
|
|
182
258
|
}
|
|
183
259
|
}
|
|
260
|
+
for (const interpretation of interpretations ?? []) {
|
|
261
|
+
const clause = clauses.find((entry) => entry.claim_key === interpretation.claim_key);
|
|
262
|
+
if (!clause)
|
|
263
|
+
continue;
|
|
264
|
+
const suggestion = ruleSuggestion(payload, interpretation);
|
|
265
|
+
if (!suggestion)
|
|
266
|
+
continue;
|
|
267
|
+
const withProvenance = withClauseProvenance(suggestion, clause, mergedClaimKeys, statementOf(payload));
|
|
268
|
+
const key = withProvenance.kind === "rule"
|
|
269
|
+
? `${withProvenance.claim_key}:rule:${withProvenance.semantic_key}`
|
|
270
|
+
: `${withProvenance.claim_key}:${withProvenance.kind}`;
|
|
271
|
+
if (!seen.has(key)) {
|
|
272
|
+
seen.add(key);
|
|
273
|
+
suggestions.push(withProvenance);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
184
276
|
return suggestions;
|
|
185
277
|
}
|
|
186
|
-
function withClauseProvenance(suggestion, clause, expectedClaimKeys) {
|
|
278
|
+
function withClauseProvenance(suggestion, clause, expectedClaimKeys, statement) {
|
|
279
|
+
const clauseOffset = statement.indexOf(clause.text);
|
|
280
|
+
const clauseSpan = utf8Span(statement || clause.text, clauseOffset >= 0 ? clauseOffset : 0, clauseOffset >= 0 ? clauseOffset + clause.text.length : clause.text.length);
|
|
187
281
|
const applyPlan = suggestion.applyPlan.map((step) => {
|
|
188
282
|
if (!isRecord(step))
|
|
189
283
|
return step;
|
|
@@ -194,11 +288,32 @@ function withClauseProvenance(suggestion, clause, expectedClaimKeys) {
|
|
|
194
288
|
const existing = Array.isArray(properties.logic_claims)
|
|
195
289
|
? properties.logic_claims.filter((value) => typeof value === "string")
|
|
196
290
|
: [];
|
|
291
|
+
const inventory = Array.isArray(properties.semantic_inventory)
|
|
292
|
+
? properties.semantic_inventory.filter(isRecord)
|
|
293
|
+
: [];
|
|
294
|
+
const status = suggestion.kind === "ambiguity_observation"
|
|
295
|
+
? "ambiguous"
|
|
296
|
+
: suggestion.kind === "ontology_gap"
|
|
297
|
+
? "ontology_gap"
|
|
298
|
+
: "modeled";
|
|
197
299
|
return {
|
|
198
300
|
...step,
|
|
199
301
|
properties: {
|
|
200
302
|
...properties,
|
|
201
303
|
logic_claims: Array.from(new Set([...existing, ...expectedClaimKeys])),
|
|
304
|
+
semantic_inventory: [
|
|
305
|
+
...inventory.filter((entry) => entry.claim_key !== clause.claim_key),
|
|
306
|
+
{
|
|
307
|
+
claim_key: clause.claim_key,
|
|
308
|
+
claim_text: clause.text,
|
|
309
|
+
role: clause.normative ? "normative" : "descriptive",
|
|
310
|
+
status,
|
|
311
|
+
span: clauseSpan,
|
|
312
|
+
reason: status === "modeled"
|
|
313
|
+
? "Draft apply plan contains a typed grounding for this proposition."
|
|
314
|
+
: suggestion.rationale,
|
|
315
|
+
},
|
|
316
|
+
],
|
|
202
317
|
},
|
|
203
318
|
};
|
|
204
319
|
}
|
|
@@ -210,6 +325,8 @@ function withClauseProvenance(suggestion, clause, expectedClaimKeys) {
|
|
|
210
325
|
...properties,
|
|
211
326
|
claim_key: clause.claim_key,
|
|
212
327
|
claim_text: clause.text,
|
|
328
|
+
claim_span_start: clauseSpan.start,
|
|
329
|
+
claim_span_end: clauseSpan.end,
|
|
213
330
|
},
|
|
214
331
|
};
|
|
215
332
|
});
|
|
@@ -225,10 +342,41 @@ function withClauseProvenance(suggestion, clause, expectedClaimKeys) {
|
|
|
225
342
|
claimKey: clause.claim_key,
|
|
226
343
|
claimText: clause.text,
|
|
227
344
|
logicClaims: expectedClaimKeys,
|
|
345
|
+
semanticInventory: [
|
|
346
|
+
{
|
|
347
|
+
claim_key: clause.claim_key,
|
|
348
|
+
claim_text: clause.text,
|
|
349
|
+
role: clause.normative ? "normative" : "descriptive",
|
|
350
|
+
status: "modeled",
|
|
351
|
+
span: clauseSpan,
|
|
352
|
+
reason: "The returned typed fact grounds this proposition.",
|
|
353
|
+
},
|
|
354
|
+
],
|
|
228
355
|
instructions: "Create the predicate fact, merge the returned logicClaims into the requirement logic_claims manifest, then add requires_predicate without overwriting other requirement metadata.",
|
|
229
356
|
},
|
|
230
357
|
}
|
|
231
358
|
: {}),
|
|
359
|
+
...(suggestion.kind === "rule" && suggestion.relationshipPlan !== null
|
|
360
|
+
? {
|
|
361
|
+
relationshipPlan: {
|
|
362
|
+
...suggestion.relationshipPlan,
|
|
363
|
+
claimKey: clause.claim_key,
|
|
364
|
+
claimText: clause.text,
|
|
365
|
+
logicClaims: expectedClaimKeys,
|
|
366
|
+
semanticInventory: [
|
|
367
|
+
{
|
|
368
|
+
claim_key: clause.claim_key,
|
|
369
|
+
claim_text: clause.text,
|
|
370
|
+
role: clause.normative ? "normative" : "descriptive",
|
|
371
|
+
status: "modeled",
|
|
372
|
+
span: clauseSpan,
|
|
373
|
+
reason: "The returned typed rule grounds this proposition.",
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
instructions: "Create the rule_schema and rule facts, merge the returned logicClaims into the requirement logic_claims manifest, then add requires_rule without overwriting other requirement metadata.",
|
|
377
|
+
},
|
|
378
|
+
}
|
|
379
|
+
: {}),
|
|
232
380
|
};
|
|
233
381
|
}
|
|
234
382
|
function unmatchedClauseSuggestion(payload, clause, expectedClaimKeys) {
|
|
@@ -246,14 +394,281 @@ function unmatchedClauseSuggestion(payload, clause, expectedClaimKeys) {
|
|
|
246
394
|
"review:ontology-gap",
|
|
247
395
|
"needs_schema_extension",
|
|
248
396
|
]),
|
|
249
|
-
}, clause, expectedClaimKeys);
|
|
397
|
+
}, clause, expectedClaimKeys, statementOf(payload));
|
|
398
|
+
}
|
|
399
|
+
function propositionRole(statement, normative) {
|
|
400
|
+
if (/\b(?:for example|e\.g\.|such as|illustrative)\b/i.test(statement))
|
|
401
|
+
return "example";
|
|
402
|
+
if (/\b(?:because|so that|in order to| rationale|therefore)\b/i.test(statement))
|
|
403
|
+
return "rationale";
|
|
404
|
+
if (/\b(?:feel|comfortable|looks complete|seems complete|subjective|prefer)\b/i.test(statement))
|
|
405
|
+
return "subjective";
|
|
406
|
+
if (/\b(?:means|defined as|refers to|is called)\b/i.test(statement))
|
|
407
|
+
return "definition";
|
|
408
|
+
if (/^\s*(?:if|when|whenever|provided that|only if)\b/i.test(statement))
|
|
409
|
+
return "condition";
|
|
410
|
+
if (/\b(?:unless|except|exempt|apart from)\b/i.test(statement))
|
|
411
|
+
return "exception";
|
|
412
|
+
if (normative)
|
|
413
|
+
return "normative";
|
|
414
|
+
return "descriptive";
|
|
415
|
+
}
|
|
416
|
+
function shadowAnalysis(text, interpretations) {
|
|
417
|
+
const valid = interpretations.filter((interpretation) => interpretation.valid);
|
|
418
|
+
const models = valid.flatMap(({ normalized_ir: ir }) => (ir ? [ir] : []));
|
|
419
|
+
const containsExpressionKind = (kind) => {
|
|
420
|
+
const visit = (value) => {
|
|
421
|
+
if (!isRecord(value))
|
|
422
|
+
return false;
|
|
423
|
+
if (value.kind === kind)
|
|
424
|
+
return true;
|
|
425
|
+
return Object.values(value).some((child) => Array.isArray(child) ? child.some(visit) : visit(child));
|
|
426
|
+
};
|
|
427
|
+
return models.some((model) => visit(model));
|
|
428
|
+
};
|
|
429
|
+
const hasModality = (modalities) => models.some((model) => modalities.includes(model.modality));
|
|
430
|
+
const hasNegativeAtom = (() => {
|
|
431
|
+
const visit = (value) => {
|
|
432
|
+
if (!isRecord(value))
|
|
433
|
+
return false;
|
|
434
|
+
if (value.kind === "not" || value.polarity === "negative")
|
|
435
|
+
return true;
|
|
436
|
+
return Object.values(value).some((child) => Array.isArray(child) ? child.some(visit) : visit(child));
|
|
437
|
+
};
|
|
438
|
+
return models.some((model) => visit(model));
|
|
439
|
+
})();
|
|
440
|
+
const cues = [
|
|
441
|
+
{
|
|
442
|
+
kind: "modal",
|
|
443
|
+
pattern: /\b(?:must|shall|should|may|can)\b/i,
|
|
444
|
+
evidence: "deontic modal",
|
|
445
|
+
represented: models.length > 0,
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
kind: "polarity",
|
|
449
|
+
pattern: /\b(?:assert|deny|forbid|allowed|permitted|prohibited)\b/i,
|
|
450
|
+
evidence: "polarity cue",
|
|
451
|
+
represented: hasModality(["deny", "forbid", "permit"]) || hasNegativeAtom,
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
kind: "passive_voice",
|
|
455
|
+
pattern: /\b(?:is|are|be|was|were)\s+[a-z]+ed\b/i,
|
|
456
|
+
evidence: "passive construction",
|
|
457
|
+
represented: false,
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
kind: "nominalization",
|
|
461
|
+
pattern: /\b[a-z]+(?:tion|ment|ity|ance|ence)\b/i,
|
|
462
|
+
evidence: "nominalized domain term",
|
|
463
|
+
represented: false,
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
kind: "conditional",
|
|
467
|
+
pattern: /\b(?:if|when|whenever|provided that)\b/i,
|
|
468
|
+
evidence: "conditional",
|
|
469
|
+
represented: models.some(({ kind, body }) => kind === "rule" && body !== undefined),
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
kind: "causal",
|
|
473
|
+
pattern: /\b(?:because|so that|therefore|as a result)\b/i,
|
|
474
|
+
evidence: "causal relation",
|
|
475
|
+
represented: false,
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
kind: "prerequisite",
|
|
479
|
+
pattern: /\b(?:before|only after|requires?\s+.+?\s+before)\b/i,
|
|
480
|
+
evidence: "prerequisite/order relation",
|
|
481
|
+
represented: containsExpressionKind("temporal"),
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
kind: "exception",
|
|
485
|
+
pattern: /\b(?:unless|except|exempt|apart from)\b/i,
|
|
486
|
+
evidence: "exception",
|
|
487
|
+
represented: models.some(({ exceptions }) => (exceptions?.length ?? 0) > 0) ||
|
|
488
|
+
containsExpressionKind("not"),
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
kind: "temporal",
|
|
492
|
+
pattern: /\b(?:before|after|during|until|within|expires?|retained for)\b/i,
|
|
493
|
+
evidence: "temporal qualifier",
|
|
494
|
+
represented: containsExpressionKind("temporal") ||
|
|
495
|
+
models.some(({ validFrom, validTo }) => validFrom !== undefined || validTo !== undefined) ||
|
|
496
|
+
containsExpressionKind("duration"),
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
kind: "quantifier",
|
|
500
|
+
pattern: /\b(?:each|every|all|any|some|only|at least|at most|exactly|no more than)\b/i,
|
|
501
|
+
evidence: "quantifier/cardinality",
|
|
502
|
+
represented: models.some(({ variables }) => (variables?.length ?? 0) > 0) ||
|
|
503
|
+
containsExpressionKind("count") ||
|
|
504
|
+
containsExpressionKind("compare") ||
|
|
505
|
+
containsExpressionKind("all") ||
|
|
506
|
+
containsExpressionKind("any"),
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
kind: "numeric",
|
|
510
|
+
pattern: /\b\d+(?:\.\d+)?\b|\b(?:zero|one|two|three|four|five|six|seven|eight|nine|ten)\b/i,
|
|
511
|
+
evidence: "numeric or cardinality value",
|
|
512
|
+
represented: containsExpressionKind("number") ||
|
|
513
|
+
containsExpressionKind("count") ||
|
|
514
|
+
containsExpressionKind("compare"),
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
kind: "negation_scope",
|
|
518
|
+
pattern: /\b(?:not|never|no|without|cannot|can't|must not)\b/i,
|
|
519
|
+
evidence: "negation scope",
|
|
520
|
+
represented: hasModality(["deny", "forbid"]) || hasNegativeAtom,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
kind: "directionality",
|
|
524
|
+
pattern: /\b(?:only if|if and only if|unless|when)\b/i,
|
|
525
|
+
evidence: "condition direction",
|
|
526
|
+
represented: models.some(({ kind, body }) => kind === "rule" && body !== undefined),
|
|
527
|
+
},
|
|
528
|
+
];
|
|
529
|
+
return cues
|
|
530
|
+
.filter((cue) => cue.pattern.test(text))
|
|
531
|
+
.map(({ kind, evidence, represented }) => ({
|
|
532
|
+
kind,
|
|
533
|
+
evidence,
|
|
534
|
+
represented,
|
|
535
|
+
}));
|
|
536
|
+
}
|
|
537
|
+
function propositionSpan(text, clause, cursor) {
|
|
538
|
+
const exact = text.indexOf(clause.text, cursor);
|
|
539
|
+
const start = exact >= 0 ? exact : cursor;
|
|
540
|
+
const end = exact >= 0
|
|
541
|
+
? exact + clause.text.length
|
|
542
|
+
: Math.min(text.length, start + clause.text.length);
|
|
543
|
+
return { span: utf8Span(text, start, end), next: Math.max(cursor, end) };
|
|
544
|
+
}
|
|
545
|
+
function interpretationResults(interpretations, clauses, statement) {
|
|
546
|
+
if (interpretations === undefined)
|
|
547
|
+
return { results: [], warnings: [] };
|
|
548
|
+
const known = new Set(clauses.map((clause) => clause.claim_key));
|
|
549
|
+
const semanticKeys = new Map();
|
|
550
|
+
const warnings = [];
|
|
551
|
+
const results = interpretations.slice(0, 3).map((candidate) => {
|
|
552
|
+
const confidence = Math.max(0, Math.min(1, candidate.confidence ?? 0.5));
|
|
553
|
+
const validation = validateLogicIr(candidate.ir);
|
|
554
|
+
const errors = [...validation.errors];
|
|
555
|
+
const clause = clauses.find((entry) => entry.claim_key === candidate.claim_key);
|
|
556
|
+
if (!known.has(candidate.claim_key))
|
|
557
|
+
errors.push(`claim_key ${candidate.claim_key} is not present in the advisor proposition inventory`);
|
|
558
|
+
if (clause &&
|
|
559
|
+
candidate.claim_text !== clause.text &&
|
|
560
|
+
semanticClaimKey(candidate.claim_text) !== clause.claim_key)
|
|
561
|
+
errors.push(`claim_text for ${candidate.claim_key} must match the proposition text (apart from normalized punctuation)`);
|
|
562
|
+
if (candidate.span &&
|
|
563
|
+
(candidate.span.start < 0 || candidate.span.end < candidate.span.start))
|
|
564
|
+
errors.push("interpretation span is invalid");
|
|
565
|
+
if (clause && candidate.span) {
|
|
566
|
+
const candidateStart = statement.indexOf(candidate.claim_text);
|
|
567
|
+
const clauseStart = statement.indexOf(clause.text);
|
|
568
|
+
const sourceStart = candidateStart >= 0 ? candidateStart : clauseStart;
|
|
569
|
+
const sourceEnd = sourceStart >= 0
|
|
570
|
+
? sourceStart +
|
|
571
|
+
(candidateStart >= 0
|
|
572
|
+
? candidate.claim_text.length
|
|
573
|
+
: clause.text.length)
|
|
574
|
+
: 0;
|
|
575
|
+
const expectedSpan = utf8Span(statement, Math.max(0, sourceStart), Math.max(0, sourceEnd));
|
|
576
|
+
if (candidate.span.start !== expectedSpan.start ||
|
|
577
|
+
candidate.span.end !== expectedSpan.end)
|
|
578
|
+
errors.push(`interpretation span for ${candidate.claim_key} does not match the exact UTF-8 proposition span`);
|
|
579
|
+
}
|
|
580
|
+
const semanticKey = validation.semanticKey;
|
|
581
|
+
if (semanticKey !== undefined) {
|
|
582
|
+
const prior = semanticKeys.get(candidate.claim_key);
|
|
583
|
+
if (prior !== undefined && prior !== semanticKey)
|
|
584
|
+
warnings.push(`Claim ${candidate.claim_key} has materially different interpretations; it remains unresolved until one is selected.`);
|
|
585
|
+
semanticKeys.set(candidate.claim_key, semanticKey);
|
|
586
|
+
}
|
|
587
|
+
return {
|
|
588
|
+
claim_key: candidate.claim_key,
|
|
589
|
+
...(semanticKey ? { semantic_key: semanticKey } : {}),
|
|
590
|
+
...(validation.normalized
|
|
591
|
+
? { normalized_ir: validation.normalized }
|
|
592
|
+
: {}),
|
|
593
|
+
valid: errors.length === 0 && validation.valid,
|
|
594
|
+
confidence,
|
|
595
|
+
errors,
|
|
596
|
+
warnings: validation.warnings,
|
|
597
|
+
...(validation.renderedProlog
|
|
598
|
+
? { rendered_prolog: validation.renderedProlog }
|
|
599
|
+
: {}),
|
|
600
|
+
};
|
|
601
|
+
});
|
|
602
|
+
if (interpretations.length > 3)
|
|
603
|
+
warnings.push("Only the first three interpretations are considered; submit materially distinct alternatives explicitly and resolve them before writing.");
|
|
604
|
+
return { results, warnings };
|
|
605
|
+
}
|
|
606
|
+
function propositionInventory(statement, clauses, suggestions, interpretations, inputPayloadHash) {
|
|
607
|
+
const interpretationKeys = new Map();
|
|
608
|
+
for (const interpretation of interpretations) {
|
|
609
|
+
if (!interpretation.valid || !interpretation.semantic_key)
|
|
610
|
+
continue;
|
|
611
|
+
const keys = interpretationKeys.get(interpretation.claim_key) ?? new Set();
|
|
612
|
+
keys.add(interpretation.semantic_key);
|
|
613
|
+
interpretationKeys.set(interpretation.claim_key, keys);
|
|
614
|
+
}
|
|
615
|
+
let cursor = 0;
|
|
616
|
+
return clauses.map((clause) => {
|
|
617
|
+
const located = propositionSpan(statement, clause, cursor);
|
|
618
|
+
cursor = located.next;
|
|
619
|
+
const role = propositionRole(clause.text, clause.normative);
|
|
620
|
+
const suggestion = suggestions.find((candidate) => candidate.claim_key === clause.claim_key);
|
|
621
|
+
let status;
|
|
622
|
+
let reason;
|
|
623
|
+
const keys = interpretationKeys.get(clause.claim_key);
|
|
624
|
+
const semanticKey = keys?.size === 1 ? [...keys][0] : undefined;
|
|
625
|
+
if (keys && keys.size > 1) {
|
|
626
|
+
status = "ambiguous";
|
|
627
|
+
reason =
|
|
628
|
+
"Materially different valid interpretations were submitted; confidence cannot choose between them.";
|
|
629
|
+
}
|
|
630
|
+
else if (semanticKey) {
|
|
631
|
+
status = "modeled";
|
|
632
|
+
}
|
|
633
|
+
else if (role === "rationale" ||
|
|
634
|
+
role === "example" ||
|
|
635
|
+
role === "subjective") {
|
|
636
|
+
status = "nonlogical";
|
|
637
|
+
reason =
|
|
638
|
+
"Prose is retained for human context but does not assert a verifiable domain proposition.";
|
|
639
|
+
}
|
|
640
|
+
else if (suggestion?.kind === "ambiguity_observation") {
|
|
641
|
+
status = "ambiguous";
|
|
642
|
+
reason = suggestion.rationale;
|
|
643
|
+
}
|
|
644
|
+
else if (suggestion?.kind === "ontology_gap") {
|
|
645
|
+
status = "ontology_gap";
|
|
646
|
+
reason = suggestion.rationale;
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
status = "missing";
|
|
650
|
+
reason =
|
|
651
|
+
"No accepted typed interpretation grounds this assertive proposition.";
|
|
652
|
+
}
|
|
653
|
+
return {
|
|
654
|
+
claim_key: clause.claim_key,
|
|
655
|
+
claim_text: clause.text,
|
|
656
|
+
role,
|
|
657
|
+
status,
|
|
658
|
+
span: located.span,
|
|
659
|
+
payload_hash: inputPayloadHash,
|
|
660
|
+
...(semanticKey ? { semantic_key: semanticKey } : {}),
|
|
661
|
+
...(reason ? { reason } : {}),
|
|
662
|
+
};
|
|
663
|
+
});
|
|
250
664
|
}
|
|
251
665
|
export function analyzeSemanticAdvisorInput(input) {
|
|
252
666
|
const payload = input.payload;
|
|
253
667
|
const requirement = isRequirement(payload);
|
|
668
|
+
const statement = statementOf(payload) || proseOf(payload);
|
|
254
669
|
const signals = requirement ? detectSignals(proseOf(payload)) : [];
|
|
255
|
-
const clauses =
|
|
256
|
-
? extractSemanticClauses(
|
|
670
|
+
const clauses = statement
|
|
671
|
+
? extractSemanticClauses(statement, input.clauses)
|
|
257
672
|
: [];
|
|
258
673
|
const rawDeclaredClaims = propertiesOf(payload).logic_claims;
|
|
259
674
|
const declaredClaims = Array.isArray(rawDeclaredClaims)
|
|
@@ -266,6 +681,13 @@ export function analyzeSemanticAdvisorInput(input) {
|
|
|
266
681
|
isModeled(payload, expectedClaims.length) &&
|
|
267
682
|
expectedClaims.length > 0 &&
|
|
268
683
|
expectedClaims.every((claimKey) => declaredClaims.includes(claimKey));
|
|
269
|
-
const suggestions = modelingSuggestions(payload, modeled, clauses);
|
|
270
|
-
|
|
684
|
+
const suggestions = modelingSuggestions(payload, modeled, clauses, input.interpretations);
|
|
685
|
+
const interpreted = interpretationResults(input.interpretations, clauses, statement);
|
|
686
|
+
const propositions = propositionInventory(statement, clauses, suggestions, interpreted.results, payloadHash(payload));
|
|
687
|
+
const shadow = shadowAnalysis(statement, interpreted.results);
|
|
688
|
+
const result = buildAdvisorResult(payload, signals, modeled, suggestions, clauses, propositions, interpreted.results, shadow);
|
|
689
|
+
return {
|
|
690
|
+
receipt: result.receipt,
|
|
691
|
+
warnings: [...result.warnings, ...interpreted.warnings],
|
|
692
|
+
};
|
|
271
693
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observation-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/observation-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,
|
|
1
|
+
{"version":3,"file":"observation-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/observation-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAMb,MAAM,aAAa,CAAC;AAGrB,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAAE,CA6B9C"}
|
|
@@ -1,10 +1,20 @@
|
|
|
1
|
-
import { shortHash, sourceOf, statementOf, stringValue, } from "./shared.js";
|
|
1
|
+
import { relationship, shortHash, sourceOf, statementOf, stringValue, } from "./shared.js";
|
|
2
2
|
// implements REQ-mcp-semantic-advisor-preflight
|
|
3
3
|
export function observationPlan(payload, title, tags) {
|
|
4
|
+
const factId = `FACT-OBS-${shortHash(`${stringValue(payload.id)}.${title}.${statementOf(payload)}`)}`;
|
|
5
|
+
const target = tags.includes("review:ambiguity")
|
|
6
|
+
? "review:ambiguous-claim"
|
|
7
|
+
: tags.includes("review:keyword-false-positive")
|
|
8
|
+
? "review:keyword-false-positive"
|
|
9
|
+
: tags.includes("review:ontology-gap")
|
|
10
|
+
? "review:ontology-gap"
|
|
11
|
+
: tags.includes("review:nonlogical")
|
|
12
|
+
? "review:nonlogical"
|
|
13
|
+
: undefined;
|
|
4
14
|
return [
|
|
5
15
|
{
|
|
6
16
|
type: "fact",
|
|
7
|
-
id:
|
|
17
|
+
id: factId,
|
|
8
18
|
properties: {
|
|
9
19
|
title,
|
|
10
20
|
status: "active",
|
|
@@ -13,7 +23,9 @@ export function observationPlan(payload, title, tags) {
|
|
|
13
23
|
text_ref: statementOf(payload),
|
|
14
24
|
tags,
|
|
15
25
|
},
|
|
16
|
-
relationships:
|
|
26
|
+
relationships: target === undefined
|
|
27
|
+
? []
|
|
28
|
+
: [relationship(factId, target, "relates_to")],
|
|
17
29
|
},
|
|
18
30
|
];
|
|
19
31
|
}
|