kibi-cli 2.9.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.
Files changed (57) hide show
  1. package/dist/operations/bootstrap/candidates.d.ts +2 -1
  2. package/dist/operations/bootstrap/candidates.d.ts.map +1 -1
  3. package/dist/operations/bootstrap/candidates.js +2 -2
  4. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  5. package/dist/operations/bootstrap/generate.js +6 -2
  6. package/dist/operations/bootstrap/intent-claims.d.ts +2 -1
  7. package/dist/operations/bootstrap/intent-claims.d.ts.map +1 -1
  8. package/dist/operations/bootstrap/intent-claims.js +27 -5
  9. package/dist/operations/bootstrap/markdown-candidates.d.ts +8 -1
  10. package/dist/operations/bootstrap/markdown-candidates.d.ts.map +1 -1
  11. package/dist/operations/bootstrap/markdown-candidates.js +59 -5
  12. package/dist/operations/bootstrap/presentation.d.ts.map +1 -1
  13. package/dist/operations/bootstrap/presentation.js +6 -0
  14. package/dist/operations/bootstrap/requirement-claims.d.ts +61 -1
  15. package/dist/operations/bootstrap/requirement-claims.d.ts.map +1 -1
  16. package/dist/operations/bootstrap/requirement-claims.js +263 -0
  17. package/dist/operations/bootstrap/types.d.ts +6 -0
  18. package/dist/operations/bootstrap/types.d.ts.map +1 -1
  19. package/dist/operations/modeling/predicate-applyplan.d.ts +7 -0
  20. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  21. package/dist/operations/modeling/predicate-applyplan.js +39 -12
  22. package/dist/operations/modeling/predicate-binding-hints.d.ts +11 -0
  23. package/dist/operations/modeling/predicate-binding-hints.d.ts.map +1 -0
  24. package/dist/operations/modeling/predicate-binding-hints.js +91 -0
  25. package/dist/operations/modeling/predicate-bindings.d.ts +24 -1
  26. package/dist/operations/modeling/predicate-bindings.d.ts.map +1 -1
  27. package/dist/operations/modeling/predicate-bindings.js +113 -3
  28. package/dist/operations/modeling/predicate-types.d.ts +19 -1
  29. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  30. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  31. package/dist/operations/modeling/suggest-predicates.js +117 -44
  32. package/dist/operations/planning/apply-plan.d.ts +8 -0
  33. package/dist/operations/planning/apply-plan.d.ts.map +1 -1
  34. package/dist/operations/planning/apply-plan.js +69 -20
  35. package/dist/operations/semantic-advisor/observation-plan.d.ts +7 -0
  36. package/dist/operations/semantic-advisor/observation-plan.d.ts.map +1 -1
  37. package/dist/operations/semantic-advisor/observation-plan.js +13 -14
  38. package/dist/public/operations/catalog.d.ts +15 -0
  39. package/dist/public/operations/catalog.d.ts.map +1 -1
  40. package/dist/public/operations/contracts.d.ts.map +1 -1
  41. package/dist/public/operations/contracts.js +5 -1
  42. package/dist/public/operations/specs/bootstrap.d.ts +15 -0
  43. package/dist/public/operations/specs/bootstrap.d.ts.map +1 -1
  44. package/dist/public/operations/specs/bootstrap.js +15 -0
  45. package/dist/public/operations/specs/planning.d.ts +1 -1
  46. package/dist/public/operations/specs/planning.d.ts.map +1 -1
  47. package/dist/public/operations/specs/planning.js +1 -1
  48. package/dist/public/skills/kibi-bootstrap/SKILL.md +5 -5
  49. package/dist/public/skills/kibi-usage/SKILL.md +20 -8
  50. package/dist/public/skills/kibi-usage/resources/workflows.md +2 -2
  51. package/package.json +1 -1
  52. package/src/public/operations/contracts.ts +6 -1
  53. package/src/public/operations/specs/bootstrap.ts +18 -0
  54. package/src/public/operations/specs/planning.ts +4 -1
  55. package/src/public/skills/kibi-bootstrap/SKILL.md +5 -5
  56. package/src/public/skills/kibi-usage/SKILL.md +20 -8
  57. package/src/public/skills/kibi-usage/resources/workflows.md +2 -2
@@ -92,12 +92,122 @@ export function isGenericPlaceholder(value) {
92
92
  (normalized.endsWith("_policy") &&
93
93
  ["executable", "cwd", "environment", "stdio", "termination"].some((kind) => normalized.startsWith(kind))));
94
94
  }
95
- export function classifyBinding(value, text, explicit, canonical = false) {
95
+ /**
96
+ * Words that carry no domain meaning on their own: auxiliary and trivial
97
+ * verbs, articles, pronouns and filler. A binding made of one of them names
98
+ * no reviewed value, so it stays unbound.
99
+ */
100
+ const STOP_WORD_BINDINGS = new Set([
101
+ "a",
102
+ "an",
103
+ "the",
104
+ "be",
105
+ "is",
106
+ "are",
107
+ "was",
108
+ "were",
109
+ "been",
110
+ "being",
111
+ "am",
112
+ "do",
113
+ "does",
114
+ "did",
115
+ "done",
116
+ "have",
117
+ "has",
118
+ "had",
119
+ "it",
120
+ "its",
121
+ "this",
122
+ "that",
123
+ "these",
124
+ "those",
125
+ "they",
126
+ "them",
127
+ "there",
128
+ "thing",
129
+ "something",
130
+ "anything",
131
+ "some",
132
+ "any",
133
+ "of",
134
+ "to",
135
+ "in",
136
+ "at",
137
+ "by",
138
+ "for",
139
+ "with",
140
+ "and",
141
+ "or",
142
+ "not",
143
+ "null",
144
+ "nil",
145
+ "n_a",
146
+ "na",
147
+ "tbd",
148
+ "todo",
149
+ "x",
150
+ "xxx",
151
+ "foo",
152
+ "bar",
153
+ "example",
154
+ "placeholder",
155
+ ]);
156
+ /** A binding value in the snake_case form argument names use. */
157
+ export function bindingToken(value) {
158
+ return value
159
+ .trim()
160
+ .toLowerCase()
161
+ .replace(/[^a-z0-9]+/g, "_")
162
+ .replace(/^_+|_+$/g, "");
163
+ }
164
+ function claimNames(text, token) {
165
+ if (!text || !token)
166
+ return false;
167
+ const words = token.split("_").filter(Boolean);
168
+ if (words.length === 0)
169
+ return false;
170
+ return new RegExp(`\\b${words.join("[\\s_-]+")}\\b`, "i").test(text);
171
+ }
172
+ function matchesConstant(value, context) {
173
+ const token = bindingToken(value);
174
+ return (context.constants?.some((constant) => bindingToken(constant) === token) ??
175
+ false);
176
+ }
177
+ /**
178
+ * Why a value only stands in for a binding, or null when it can be a reviewed
179
+ * value: it repeats its own or another argument's name, or it is a bare stop
180
+ * word. Declared constants and the booleans `true`/`false` always pass.
181
+ */
182
+ // implements REQ-model-predicates-binding-placeholders
183
+ export function nameOrStopWordReason(value, context = {}) {
184
+ const token = bindingToken(value);
185
+ if (token === "true" || token === "false")
186
+ return null;
187
+ if (matchesConstant(value, context))
188
+ return null;
189
+ if (STOP_WORD_BINDINGS.has(token))
190
+ return `"${token}" is a stop word`;
191
+ const own = context.argumentName ? bindingToken(context.argumentName) : "";
192
+ const repeatsName = (own !== "" && token === own) ||
193
+ (context.argumentNames ?? []).some((name) => bindingToken(name) === token);
194
+ if (repeatsName && !claimNames(context.text, token))
195
+ return `it repeats the argument name ${token} and the claim does not name it`;
196
+ return null;
197
+ }
198
+ // implements REQ-mcp-suggest-predicates, REQ-model-predicates-binding-placeholders
199
+ export function classifyBinding(value, text, explicit, canonical = false, context = {}) {
96
200
  const normalized = value.trim();
97
- if (canonical && !isGenericPlaceholder(normalized))
201
+ // A value that only repeats an argument name or a stop word is unbound
202
+ // however it was supplied or extracted.
203
+ if (nameOrStopWordReason(normalized, { ...context, text }) !== null)
204
+ return "placeholder";
205
+ const constant = matchesConstant(normalized, context);
206
+ if (canonical && (constant || !isGenericPlaceholder(normalized)))
98
207
  return "extracted";
99
208
  if (explicit &&
100
- (!isGenericPlaceholder(normalized) ||
209
+ (constant ||
210
+ !isGenericPlaceholder(normalized) ||
101
211
  ["true", "false"].includes(normalized.toLowerCase())))
102
212
  return "explicit";
103
213
  if (!normalized || isGenericPlaceholder(normalized))
@@ -86,6 +86,22 @@ export interface RecommendedPredicateSchema {
86
86
  rationale: string;
87
87
  reuse_scope: string;
88
88
  }
89
+ /**
90
+ * What an agent needs to bind one unbound predicate argument: its type, the
91
+ * declared constants of a closed vocabulary, schema example values and why
92
+ * the current value was not accepted.
93
+ */
94
+ export interface BindingHint {
95
+ argument: string;
96
+ position: number;
97
+ type: string;
98
+ description?: string;
99
+ allowedValues?: string[];
100
+ examples: string[];
101
+ currentValue: string;
102
+ provenance: BindingProvenance;
103
+ reason: string;
104
+ }
89
105
  /** A logical grounding relationship an existing requirement has for a claim. */
90
106
  export interface ExistingClaimGrounding {
91
107
  relationship: {
@@ -110,7 +126,7 @@ export interface SuggestPredicatesResult {
110
126
  requirementId: string | null;
111
127
  subject: string;
112
128
  candidates: PredicateSuggestion[];
113
- recommendedAction: "apply_requires_predicate" | "provide_argument_bindings" | "resolve_schema_reference" | "record_ontology_gap" | "review_nonlogical" | "already_grounded";
129
+ recommendedAction: "apply_requires_predicate" | "provide_argument_bindings" | "resolve_schema_reference" | "record_ontology_gap" | "review_nonlogical" | "replace_grounding";
114
130
  recommendedPredicateSchema: RecommendedPredicateSchema | null;
115
131
  applyPlan: Array<Record<string, unknown>>;
116
132
  relationshipPlan: Record<string, unknown> | null;
@@ -118,6 +134,8 @@ export interface SuggestPredicatesResult {
118
134
  relationshipTarget?: string | null;
119
135
  existingGrounding?: ExistingClaimGrounding[];
120
136
  replacementPlan?: Record<string, unknown> | null;
137
+ /** On provide_argument_bindings: one hint per unbound argument of the recommended candidate. */
138
+ bindingHints?: BindingHint[];
121
139
  warnings: string[];
122
140
  };
123
141
  applyPlan: Array<Record<string, unknown>>;
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-types.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"predicate-types.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"suggest-predicates.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/suggest-predicates.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACX,MAAM,0CAA0C,CAAC;AAqBlD,OAAO,KAAK,EAGV,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AA6H9B,wBAAgB,sCAAsC,CACpD,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,GAAG,gBAAgB,CAAC,EACzE,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,GAAG,gBAAgB,CAAC,GACzE,MAAM,CAIR;AAED,wBAAgB,2CAA2C,CACzD,IAAI,EAAE,IAAI,CACR,mBAAmB,EACnB,qBAAqB,GAAG,OAAO,GAAG,gBAAgB,CACnD,EACD,KAAK,EAAE,IAAI,CACT,mBAAmB,EACnB,qBAAqB,GAAG,OAAO,GAAG,gBAAgB,CACnD,GACA,MAAM,CAKR;AA0FD,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,IAAI,EAAE,qBAAqB,EAC3B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CA2OlC;AA8CD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAElC"}
1
+ {"version":3,"file":"suggest-predicates.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/suggest-predicates.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACX,MAAM,0CAA0C,CAAC;AAyBlD,OAAO,KAAK,EAGV,mBAAmB,EACnB,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,sBAAsB,CAAC;AAG9B,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,mBAAmB,EACnB,0BAA0B,EAC1B,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AA6H9B,wBAAgB,sCAAsC,CACpD,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,GAAG,gBAAgB,CAAC,EACzE,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,qBAAqB,GAAG,gBAAgB,CAAC,GACzE,MAAM,CAIR;AAED,wBAAgB,2CAA2C,CACzD,IAAI,EAAE,IAAI,CACR,mBAAmB,EACnB,qBAAqB,GAAG,OAAO,GAAG,gBAAgB,CACnD,EACD,KAAK,EAAE,IAAI,CACT,mBAAmB,EACnB,qBAAqB,GAAG,OAAO,GAAG,gBAAgB,CACnD,GACA,MAAM,CAKR;AA0FD,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,UAAU,GAAG,IAAI,EACzB,IAAI,EAAE,qBAAqB,EAC3B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,uBAAuB,CAAC,CAmQlC;AA8GD,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,uBAAuB,CAAC,CAElC"}
@@ -1,10 +1,12 @@
1
1
  import { escapeAtom } from "../../prolog/codec.js";
2
+ import { loadEntities } from "../../public/operations/discovery-entities.js";
2
3
  import { parsePrologList } from "../mutation/serialization.js";
3
4
  import { analyzeSemanticAdvisorInput } from "../semantic-advisor/analyze-prose.js";
4
5
  import { semanticClaimKey } from "../semantic-advisor/clauses.js";
5
6
  import { composeOntologyCatalogForOperation } from "../semantic-advisor/plugin-orchestration.js";
6
7
  import { STRONG_APPLICABILITY_SCORE, WEAK_CANDIDATE_MARGIN, evaluateSemanticApplicability, } from "./predicate-applicability.js";
7
8
  import { buildGapApplyPlan, buildPredicateApplyPlan, buildPredicateSchemaDraft, buildRelationshipPlan, buildSuggestion, } from "./predicate-applyplan.js";
9
+ import { buildBindingHints, describeBindingHints, } from "./predicate-binding-hints.js";
8
10
  import { BUILT_IN_PREDICATE_SCHEMAS } from "./predicate-catalog.js";
9
11
  import { inferSubject } from "./predicate-inference.js";
10
12
  import { loadExistingPredicateSchemas } from "./predicate-loader.js";
@@ -159,7 +161,7 @@ function unquoted(value) {
159
161
  * second link (for example requires_predicate beside a bootstrap
160
162
  * requires_property) fails the proposition-complete rule.
161
163
  */
162
- // implements REQ-model-predicates-grounding-aware
164
+ // implements REQ-model-predicates-grounding-aware-v2
163
165
  async function existingClaimGrounding(prolog, requirementId, claimKey, warnings) {
164
166
  if (prolog === null || !requirementId)
165
167
  return [];
@@ -287,53 +289,69 @@ export async function handleKbSuggestPredicates(prolog, args, context) {
287
289
  const completeCandidate = recommendedCandidate && recommendedCandidate.binding_status === "complete"
288
290
  ? recommendedCandidate
289
291
  : undefined;
290
- const recommendedAction = alreadyGrounded
291
- ? "already_grounded"
292
- : !recommendedCandidate
293
- ? unavailableSchema
294
- ? "resolve_schema_reference"
295
- : "record_ontology_gap"
296
- : completeCandidate
297
- ? "apply_requires_predicate"
298
- : "provide_argument_bindings";
292
+ // The action names the predicate state; existingGrounding (non-empty) is
293
+ // the separate "already grounded" signal. A complete candidate for a claim
294
+ // that is already grounded becomes a replacement, never a second link.
295
+ const recommendedAction = !recommendedCandidate
296
+ ? unavailableSchema
297
+ ? "resolve_schema_reference"
298
+ : "record_ontology_gap"
299
+ : completeCandidate
300
+ ? alreadyGrounded
301
+ ? "replace_grounding"
302
+ : "apply_requires_predicate"
303
+ : "provide_argument_bindings";
299
304
  const predicatePlan = completeCandidate
300
305
  ? buildPredicateApplyPlan(completeCandidate, args)
301
306
  : [];
302
- // An already grounded claim gets no write plan: a second grounding link
303
- // would fail the proposition-complete rule. The replacement plan below
304
- // swaps the grounding instead.
305
- const applyPlan = alreadyGrounded
306
- ? []
307
- : completeCandidate
308
- ? predicatePlan
309
- : !recommendedCandidate && !unavailableSchema
310
- ? buildGapApplyPlan(text, args)
311
- : [];
307
+ // An already grounded claim gets no predicate write plan: a second
308
+ // grounding link would fail the proposition-complete rule, so the
309
+ // replacement plan below swaps the grounding instead. The ontology-gap
310
+ // observation is not a grounding relationship, so it is planned either way.
311
+ const applyPlan = completeCandidate
312
+ ? alreadyGrounded
313
+ ? []
314
+ : predicatePlan
315
+ : !recommendedCandidate && !unavailableSchema
316
+ ? buildGapApplyPlan(text, args)
317
+ : [];
312
318
  const plannedFactId = typeof predicatePlan[0]?.id === "string" ? predicatePlan[0].id : null;
313
319
  const relationshipPlan = completeCandidate && !alreadyGrounded
314
320
  ? buildRelationshipPlan(plannedFactId ?? "", args.requirementId, text, args.existingLogicClaims)
315
321
  : null;
316
322
  const replacementPlan = alreadyGrounded && plannedFactId !== null && args.requirementId
317
- ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding)
323
+ ? buildGroundingReplacementPlan(args.requirementId, plannedFactId, predicatePlan, existingGrounding, await storedRequirementProperties(prolog, args.requirementId, warnings))
318
324
  : null;
319
325
  const relationshipTarget = relationshipPlan !== null || replacementPlan !== null
320
326
  ? plannedFactId
321
327
  : null;
328
+ const groundingSummary = existingGrounding
329
+ .map((row) => `${row.relationship.type} -> ${row.factId}`)
330
+ .join(", ");
322
331
  if (alreadyGrounded) {
323
- warnings.push(`${args.requirementId} already grounds this claim (${claimKey}) through ${existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ")}. A modeled claim takes exactly one logical grounding relationship, so adding requires_predicate beside it fails the proposition-complete rule. Keep the existing grounding, or follow replacementPlan to swap it for the predicate.`);
332
+ warnings.push(`${args.requirementId} already grounds this claim (${claimKey}) through ${groundingSummary}. A modeled claim takes exactly one logical grounding relationship, so adding requires_predicate beside it fails the proposition-complete rule. ${replacementPlan ? "Keep the existing grounding, or follow replacementPlan to swap it for the predicate." : "Keep the existing grounding; no predicate replacement is available yet."}`);
324
333
  }
325
- const recommendedPredicateSchema = !recommendedCandidate && !unavailableSchema && !alreadyGrounded
334
+ const bindingHints = recommendedAction === "provide_argument_bindings" && recommendedCandidate
335
+ ? buildBindingHints(recommendedCandidate, text)
336
+ : [];
337
+ const recommendedPredicateSchema = !recommendedCandidate && !unavailableSchema
326
338
  ? buildPredicateSchemaDraft(text, subject)
327
339
  : null;
328
- const textSummary = alreadyGrounded
329
- ? `${args.requirementId} already grounds this claim through ${existingGrounding.map((row) => `${row.relationship.type} -> ${row.factId}`).join(", ")}; no write plan was generated.${replacementPlan ? ` To use ${completeCandidate?.predicate_name} instead, follow replacementPlan and link requires_predicate to ${plannedFactId}.` : ""}`
330
- : completeCandidate
331
- ? `Suggested ${candidates.length} predicate candidate(s). Top applicable match: ${completeCandidate.predicate_name}. Apply the predicate fact ${plannedFactId}, then link requires_predicate to that fact id (not a candidate id).`
332
- : recommendedCandidate
333
- ? `Matched ${recommendedCandidate.predicate_name}, but exact reviewed values are still required for: ${recommendedCandidate.unbound_arguments.join(", ")}. No apply plan was generated.`
334
- : unavailableSchema
335
- ? `Requested predicate schema ${args.schemaId} is unavailable or semantically inapplicable. No apply plan was generated.`
336
- : "No predicate candidate passed the semantic applicability gate; record an ontology gap and review the generated schema draft instead of silently writing prose.";
340
+ const groundedPrefix = alreadyGrounded
341
+ ? `${args.requirementId} already grounds this claim through ${groundingSummary}. `
342
+ : "";
343
+ const textSummary = groundedPrefix +
344
+ (replacementPlan
345
+ ? `To use ${completeCandidate?.predicate_name} instead, follow replacementPlan and link requires_predicate to ${plannedFactId}; otherwise keep the existing grounding.`
346
+ : completeCandidate
347
+ ? `Suggested ${candidates.length} predicate candidate(s). Top applicable match: ${completeCandidate.predicate_name}. Apply the predicate fact ${plannedFactId}, then link requires_predicate to that fact id (not a candidate id).`
348
+ : recommendedCandidate
349
+ ? `Matched ${recommendedCandidate.predicate_name}, but exact reviewed values are still required for: ${recommendedCandidate.unbound_arguments.join(", ")}. Bind them from the claim text: ${describeBindingHints(bindingHints)}. A value that repeats an argument name or a stop word stays unbound. No apply plan was generated.`
350
+ : unavailableSchema
351
+ ? `Requested predicate schema ${args.schemaId} is unavailable or semantically inapplicable. No apply plan was generated.`
352
+ : alreadyGrounded
353
+ ? "No predicate candidate passed the semantic applicability gate; keep the existing grounding and record the ontology-gap observation in applyPlan so the missing schema stays visible."
354
+ : "No predicate candidate passed the semantic applicability gate; record an ontology gap and review the generated schema draft instead of silently writing prose.");
337
355
  const logicClaims = Array.from(new Set([...(args.existingLogicClaims ?? []), claimKey]));
338
356
  return {
339
357
  content: [{ type: "text", text: textSummary }],
@@ -352,18 +370,72 @@ export async function handleKbSuggestPredicates(prolog, args, context) {
352
370
  relationshipTarget,
353
371
  existingGrounding,
354
372
  replacementPlan,
373
+ bindingHints,
355
374
  warnings,
356
375
  },
357
376
  applyPlan,
358
377
  };
359
378
  }
379
+ /** Stored requirement fields a relationship-only update must restate. */
380
+ const RESTATED_REQUIREMENT_FIELDS = [
381
+ "title",
382
+ "status",
383
+ "priority",
384
+ "owner",
385
+ "tags",
386
+ ];
387
+ /**
388
+ * The requirement fields the replacement's requirement upsert restates, so
389
+ * the step is a complete kb_upsert payload (title and status are required)
390
+ * that keeps the stored metadata. The stored proposition ledger and text_ref
391
+ * are merged by kb_upsert itself.
392
+ */
393
+ // implements REQ-model-predicates-plan-roundtrip
394
+ async function storedRequirementProperties(prolog, requirementId, warnings) {
395
+ if (prolog === null)
396
+ return null;
397
+ try {
398
+ const [stored] = await loadEntities(prolog, {
399
+ id: requirementId,
400
+ type: "req",
401
+ });
402
+ if (stored === undefined)
403
+ return null;
404
+ const restated = {};
405
+ for (const field of RESTATED_REQUIREMENT_FIELDS)
406
+ if (stored[field] !== undefined && stored[field] !== null)
407
+ restated[field] = stored[field];
408
+ return typeof restated.title === "string" &&
409
+ typeof restated.status === "string"
410
+ ? restated
411
+ : null;
412
+ }
413
+ catch (error) {
414
+ warnings.push(`Requirement ${requirementId} could not be read (${error instanceof Error ? error.message : String(error)}); add its stored title and status to the last replacementPlan step before applying it.`);
415
+ return null;
416
+ }
417
+ }
360
418
  /**
361
419
  * Ordered steps that swap an existing grounding for the planned predicate
362
420
  * fact while keeping one grounding relationship per modeled claim: write the
363
- * fact, retract the old grounding link, then link requires_predicate.
421
+ * fact, retract the old grounding link, then link requires_predicate. The
422
+ * order is forced: kb_upsert merges a requirement's relationships, so linking
423
+ * first would give the claim two groundings and be rejected. Between the
424
+ * retraction and the link the claim is ungrounded (kb_check reports
425
+ * logic-coverage), so the steps run back to back and `rollback` restores the
426
+ * old link if the last step fails.
364
427
  */
365
- // implements REQ-model-predicates-grounding-aware
366
- function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing) {
428
+ // implements REQ-model-predicates-grounding-aware-v2, REQ-model-predicates-plan-roundtrip
429
+ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, existing, stored) {
430
+ const requirementUpsert = (relationships) => ({
431
+ type: "req",
432
+ id: requirementId,
433
+ ...(stored === null ? {} : { properties: stored }),
434
+ relationships,
435
+ });
436
+ const restate = stored === null
437
+ ? `add the requirement's stored title and status as properties (they could not be read), keep this relationship only`
438
+ : "the properties restate the stored title, status and metadata; kb_upsert keeps the stored proposition ledger";
367
439
  return {
368
440
  relationshipTarget: factId,
369
441
  steps: [
@@ -381,17 +453,18 @@ function buildGroundingReplacementPlan(requirementId, factId, predicatePlan, exi
381
453
  },
382
454
  {
383
455
  operation: "kb_upsert",
384
- input: {
385
- type: "req",
386
- id: requirementId,
387
- relationships: [
388
- { type: "requires_predicate", from: requirementId, to: factId },
389
- ],
390
- },
391
- reason: `Link requires_predicate to ${factId}; upsert the requirement with its stored title and status and this relationship only.`,
456
+ input: requirementUpsert([
457
+ { type: "requires_predicate", from: requirementId, to: factId },
458
+ ]),
459
+ reason: `Link requires_predicate to ${factId}; ${restate}.`,
392
460
  },
393
461
  ],
394
- instructions: `Only replace the grounding when the predicate states the claim at least as precisely as ${existing.map((row) => row.factId).join(", ")}. Run kb_check after the last step.`,
462
+ rollback: {
463
+ operation: "kb_upsert",
464
+ input: requirementUpsert(existing.map((row) => row.relationship)),
465
+ reason: "Only if the last step fails: restore the retracted grounding link so the claim is grounded again.",
466
+ },
467
+ instructions: `Only replace the grounding when the predicate states the claim at least as precisely as ${existing.map((row) => row.factId).join(", ")}. Apply the steps unchanged, in order and back to back: linking requires_predicate before the retraction is rejected because the claim would have two groundings, and between the retraction and the link the claim is ungrounded, so kb_check reports logic-coverage for ${requirementId} until the last step lands. If the last step fails, apply rollback. Run kb_check after the last step.`,
395
468
  };
396
469
  }
397
470
  export async function executeSuggestPredicates(args, context) {
@@ -9,6 +9,14 @@ export declare function assertBootstrapRecoveryDependencies(remaining: readonly
9
9
  dependsOn?: readonly string[];
10
10
  }[], applied: ReadonlySet<string>): void;
11
11
  export declare function orderBootstrapActions(actions: readonly BootstrapAction[], completed?: Set<string>): BootstrapAction[];
12
+ /**
13
+ * Checks a plan-bearing kb_apply_plan call can start: the approved hash, the
14
+ * plan shape and canonical hash, and for a bootstrap plan its live snapshots.
15
+ * Writes nothing. An MCP async apply runs it before returning the job
16
+ * receipt, so input and stale-plan errors fail the call instead of the job.
17
+ * The apply itself repeats every check under the workspace lock.
18
+ */
19
+ export declare function preflightApplyPlan(args: ApplyPlanArgs, context: OperationContext): Promise<void>;
12
20
  export declare function executeApplyPlan(args: ApplyPlanArgs, context: OperationContext): Promise<{
13
21
  content: Array<{
14
22
  type: "text";
@@ -1 +1 @@
1
- {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AAQlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AA+D/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AAo9ED,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,eAAe,CAAC;CACpC,CAAC,CAkDD"}
1
+ {"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/planning/apply-plan.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAEV,gBAAgB,EAGjB,MAAM,0CAA0C,CAAC;AAQlD,OAAO,EACL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AA+D/B,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,aAAa,EACb,eAAe,EACf,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,yBAAyB,EAAG,2BAAoC,CAAC;AA6G9E,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAMN;AAED,wBAAgB,mCAAmC,CACjD,SAAS,EAAE,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EACnE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,IAAI,CAaN;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,SAAS,cAAoB,GAC5B,eAAe,EAAE,CA2CnB;AA8zBD;;;;;;GAMG;AAEH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAyqDD,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC;IACT,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,iBAAiB,EAAE,eAAe,CAAC;CACpC,CAAC,CAkDD"}
@@ -675,15 +675,12 @@ async function executeSourceRecovery(args, context, onCommitted = () => undefine
675
675
  },
676
676
  };
677
677
  }
678
- async function executeBootstrapPlan(args, context, recovery = false, remainingActions, priorResults = [], onCommitted = () => undefined, carry = {}) {
679
- const actions = recovery
680
- ? [...(remainingActions ?? args.plan.actions)]
681
- : validateBootstrapPlanShape(args);
682
- preflightBootstrapActions(actions, context);
683
- const prolog = context.prolog ?? (await context.ensureProlog?.());
684
- if (!prolog)
685
- throw new Error("Bootstrap apply requires a Prolog runtime");
686
- const operationContext = { ...context, prolog, sourceFirst: true };
678
+ /**
679
+ * The bootstrap plan's live-state checks: branch, KB snapshot, workspace
680
+ * snapshot and declared source hashes must still match the plan.
681
+ */
682
+ // implements REQ-bootstrap-apply-long-running
683
+ async function assertBootstrapPlanCurrent(args, operationContext) {
687
684
  const statusResult = await executeStatus({}, operationContext);
688
685
  const status = statusResult.structuredContent;
689
686
  if (!status)
@@ -698,18 +695,70 @@ async function executeBootstrapPlan(args, context, recovery = false, remainingAc
698
695
  sourceHashes: args.plan.expected.sourceHashes,
699
696
  })
700
697
  : status.snapshotId;
701
- if (!recovery) {
702
- if (args.plan.expected.branch !== "unknown" &&
703
- status.branch !== args.plan.expected.branch)
704
- throw new Error("Bootstrap apply failed: branch changed since planning");
705
- if (boundLiveKbSnapshot !== args.plan.expected.kbSnapshotId)
706
- throw new Error("Bootstrap apply failed: KB snapshot changed since planning");
707
- if (args.plan.expected.workspaceSnapshot !== "unknown" &&
708
- (!workspace.available ||
709
- workspace.snapshot.hash !== args.plan.expected.workspaceSnapshot))
710
- throw new Error("Bootstrap apply failed: workspace snapshot changed since planning");
711
- await validateSources(operationContext, args.plan.expected.sourceHashes);
698
+ if (args.plan.expected.branch !== "unknown" &&
699
+ status.branch !== args.plan.expected.branch)
700
+ throw new Error("Bootstrap apply failed: branch changed since planning");
701
+ if (boundLiveKbSnapshot !== args.plan.expected.kbSnapshotId)
702
+ throw new Error("Bootstrap apply failed: KB snapshot changed since planning");
703
+ if (args.plan.expected.workspaceSnapshot !== "unknown" &&
704
+ (!workspace.available ||
705
+ workspace.snapshot.hash !== args.plan.expected.workspaceSnapshot))
706
+ throw new Error("Bootstrap apply failed: workspace snapshot changed since planning");
707
+ await validateSources(operationContext, args.plan.expected.sourceHashes);
708
+ return status;
709
+ }
710
+ /**
711
+ * Checks a plan-bearing kb_apply_plan call can start: the approved hash, the
712
+ * plan shape and canonical hash, and for a bootstrap plan its live snapshots.
713
+ * Writes nothing. An MCP async apply runs it before returning the job
714
+ * receipt, so input and stale-plan errors fail the call instead of the job.
715
+ * The apply itself repeats every check under the workspace lock.
716
+ */
717
+ // implements REQ-mcp-apply-plan-async-preflight
718
+ export async function preflightApplyPlan(args, context) {
719
+ if (!("plan" in args))
720
+ return;
721
+ if (!isRecord(args.plan))
722
+ throw new Error("Apply plan failed: plan must be an object");
723
+ if (typeof args.approvedPlanHash !== "string" ||
724
+ !/^[a-f0-9]{64}$/i.test(args.approvedPlanHash))
725
+ throw new Error("Apply plan failed: approvedPlanHash must be the SHA-256 planHash returned with the plan");
726
+ if (isBootstrapApplyArgs(args)) {
727
+ preflightBootstrapActions(validateBootstrapPlanShape(args), context);
728
+ const prolog = context.prolog ?? (await context.ensureProlog?.());
729
+ if (!prolog)
730
+ throw new Error("Bootstrap apply requires a Prolog runtime");
731
+ await assertBootstrapPlanCurrent(args, {
732
+ ...context,
733
+ prolog,
734
+ sourceFirst: true,
735
+ });
736
+ return;
737
+ }
738
+ if (isMigrationApplyArgs(args)) {
739
+ validateMigrationPlanShape(args);
740
+ return;
712
741
  }
742
+ if (isEntityDeletionApplyArgs(args)) {
743
+ validateEntityDeletionPlan(args);
744
+ return;
745
+ }
746
+ validateCompilePlanShape(args);
747
+ }
748
+ async function executeBootstrapPlan(args, context, recovery = false, remainingActions, priorResults = [], onCommitted = () => undefined, carry = {}) {
749
+ const actions = recovery
750
+ ? [...(remainingActions ?? args.plan.actions)]
751
+ : validateBootstrapPlanShape(args);
752
+ preflightBootstrapActions(actions, context);
753
+ const prolog = context.prolog ?? (await context.ensureProlog?.());
754
+ if (!prolog)
755
+ throw new Error("Bootstrap apply requires a Prolog runtime");
756
+ const operationContext = { ...context, prolog, sourceFirst: true };
757
+ const status = recovery
758
+ ? (await executeStatus({}, operationContext)).structuredContent
759
+ : await assertBootstrapPlanCurrent(args, operationContext);
760
+ if (!status)
761
+ throw new Error("Bootstrap apply failed: status returned no payload");
713
762
  const journalId = `bootstrap-${args.plan.planHash.slice(0, 16)}`;
714
763
  const journalPath = path.join(context.workspaceRoot, ".kb", "recovery", `${journalId}.json`);
715
764
  if (!recovery && context.fs) {
@@ -1,3 +1,10 @@
1
1
  import { type Payload } from "./shared.js";
2
+ /**
3
+ * A review observation the advisor suggests for unresolved prose. The review
4
+ * tags (`review:ambiguity`, `review:ontology-gap`, ...) stay in `tags`: a tag
5
+ * is not a KB entity, so the plan carries no relationship to it, and the
6
+ * step explains itself in `document.body` (an observation without body
7
+ * context fails entity-context-missing), so kb_upsert accepts it as returned.
8
+ */
2
9
  export declare function observationPlan(payload: Payload, title: string, tags: readonly string[]): readonly Readonly<Record<string, unknown>>[];
3
10
  //# sourceMappingURL=observation-plan.d.ts.map
@@ -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,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
+ {"version":3,"file":"observation-plan.d.ts","sourceRoot":"","sources":["../../../src/operations/semantic-advisor/observation-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,OAAO,EAKb,MAAM,aAAa,CAAC;AAGrB;;;;;;GAMG;AACH,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,CAoB9C"}
@@ -1,16 +1,14 @@
1
- import { relationship, shortHash, sourceOf, statementOf, stringValue, } from "./shared.js";
2
- // implements REQ-mcp-semantic-advisor-preflight
1
+ import { shortHash, sourceOf, statementOf, stringValue, } from "./shared.js";
2
+ // implements REQ-mcp-semantic-advisor-preflight, REQ-model-predicates-plan-roundtrip
3
+ /**
4
+ * A review observation the advisor suggests for unresolved prose. The review
5
+ * tags (`review:ambiguity`, `review:ontology-gap`, ...) stay in `tags`: a tag
6
+ * is not a KB entity, so the plan carries no relationship to it, and the
7
+ * step explains itself in `document.body` (an observation without body
8
+ * context fails entity-context-missing), so kb_upsert accepts it as returned.
9
+ */
3
10
  export function observationPlan(payload, title, tags) {
4
11
  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;
14
12
  return [
15
13
  {
16
14
  type: "fact",
@@ -23,9 +21,10 @@ export function observationPlan(payload, title, tags) {
23
21
  text_ref: statementOf(payload),
24
22
  tags,
25
23
  },
26
- relationships: target === undefined
27
- ? []
28
- : [relationship(factId, target, "relates_to")],
24
+ document: {
25
+ body: `${title}. The semantic advisor suggested this review observation because the prose below is not safely grounded by a typed fact; it stays open until a reviewer models, rewords or dismisses the claim.\n\n> ${statementOf(payload).trim().replace(/\n/g, "\n> ")}`,
26
+ },
27
+ relationships: [],
29
28
  },
30
29
  ];
31
30
  }