kibi-cli 2.11.0 → 2.13.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 (50) hide show
  1. package/dist/commands/doctor.d.ts.map +1 -1
  2. package/dist/commands/doctor.js +76 -1
  3. package/dist/engine.d.ts +27 -4
  4. package/dist/engine.d.ts.map +1 -1
  5. package/dist/engine.js +85 -28
  6. package/dist/operations/bootstrap/generate.d.ts.map +1 -1
  7. package/dist/operations/bootstrap/generate.js +5 -2
  8. package/dist/operations/bootstrap/requirement-claims.d.ts +15 -0
  9. package/dist/operations/bootstrap/requirement-claims.d.ts.map +1 -1
  10. package/dist/operations/bootstrap/requirement-claims.js +100 -1
  11. package/dist/operations/modeling/predicate-applyplan.d.ts +13 -0
  12. package/dist/operations/modeling/predicate-applyplan.d.ts.map +1 -1
  13. package/dist/operations/modeling/predicate-applyplan.js +72 -20
  14. package/dist/operations/modeling/predicate-binding-hints.d.ts +1 -1
  15. package/dist/operations/modeling/predicate-binding-hints.d.ts.map +1 -1
  16. package/dist/operations/modeling/predicate-binding-hints.js +41 -5
  17. package/dist/operations/modeling/predicate-bindings.d.ts +13 -0
  18. package/dist/operations/modeling/predicate-bindings.d.ts.map +1 -1
  19. package/dist/operations/modeling/predicate-bindings.js +56 -2
  20. package/dist/operations/modeling/predicate-catalog-1.d.ts.map +1 -1
  21. package/dist/operations/modeling/predicate-catalog-1.js +3 -0
  22. package/dist/operations/modeling/predicate-catalog-2.d.ts.map +1 -1
  23. package/dist/operations/modeling/predicate-catalog-2.js +3 -0
  24. package/dist/operations/modeling/predicate-catalog-3.d.ts.map +1 -1
  25. package/dist/operations/modeling/predicate-catalog-3.js +7 -0
  26. package/dist/operations/modeling/predicate-catalog-4.d.ts.map +1 -1
  27. package/dist/operations/modeling/predicate-catalog-4.js +2 -0
  28. package/dist/operations/modeling/predicate-closed-vocabularies.d.ts +104 -0
  29. package/dist/operations/modeling/predicate-closed-vocabularies.d.ts.map +1 -0
  30. package/dist/operations/modeling/predicate-closed-vocabularies.js +104 -0
  31. package/dist/operations/modeling/predicate-types.d.ts +13 -1
  32. package/dist/operations/modeling/predicate-types.d.ts.map +1 -1
  33. package/dist/operations/modeling/predicate-utils.d.ts +11 -1
  34. package/dist/operations/modeling/predicate-utils.d.ts.map +1 -1
  35. package/dist/operations/modeling/predicate-utils.js +19 -3
  36. package/dist/operations/modeling/suggest-predicates.d.ts.map +1 -1
  37. package/dist/operations/modeling/suggest-predicates.js +57 -3
  38. package/dist/package-versions.d.ts +10 -0
  39. package/dist/package-versions.d.ts.map +1 -0
  40. package/dist/package-versions.js +68 -0
  41. package/dist/prolog.d.ts.map +1 -1
  42. package/dist/prolog.js +4 -19
  43. package/dist/public/skills/kibi-bootstrap/SKILL.md +2 -2
  44. package/dist/public/skills/kibi-usage/SKILL.md +12 -3
  45. package/dist/utils/strict-modeling.d.ts +1 -0
  46. package/dist/utils/strict-modeling.d.ts.map +1 -1
  47. package/dist/utils/strict-modeling.js +1 -1
  48. package/package.json +2 -2
  49. package/src/public/skills/kibi-bootstrap/SKILL.md +2 -2
  50. package/src/public/skills/kibi-usage/SKILL.md +12 -3
@@ -5,12 +5,28 @@ import { inferArgs } from "./predicate-inference.js";
5
5
  import { schemaForCandidate } from "./predicate-loader.js";
6
6
  import { hashId } from "./predicate-utils.js";
7
7
  import { applyArgumentRewrites, predicateArgumentConformance, } from "./predicate-vocabulary.js";
8
- // implements REQ-mcp-suggest-predicates
8
+ // implements REQ-mcp-suggest-predicates, REQ-model-predicates-requirement-subject-v2, REQ-model-predicates-binding-clauses
9
9
  export function buildSuggestion(schema, text, subject, score, argumentBindings = {}, polarityHint, diagnostics) {
10
10
  const inferredArgs = inferArgs(schema, text, subject);
11
11
  const canonicalLauncherArgs = exactLauncherPredicateArgs(schema.predicate_name, text);
12
+ const hasExactBinding = (name) => typeof argumentBindings[name] === "string" &&
13
+ argumentBindings[name].trim().length > 0;
14
+ // The argument the schema names `subject`, wherever it sits; -1 when the
15
+ // schema names no subject (permission_rule(actor, action, resource, ...)).
16
+ const subjectArgument = schema.argument_names.indexOf("subject");
17
+ const explicitSubject = diagnostics?.explicitSubject === true;
18
+ // The reviewed subject: subjectHint, else the requirement's constrained
19
+ // subject. It binds the `subject` argument unless that argument is bound.
20
+ const reviewedSubject = explicitSubject
21
+ ? subject
22
+ : diagnostics?.requirementSubject;
23
+ const reviewedSubjectArgument = reviewedSubject !== undefined && !hasExactBinding("subject")
24
+ ? subjectArgument
25
+ : -1;
12
26
  const boundArgs = schema.argument_names.map((name, index) => {
13
27
  const exactBinding = argumentBindings[name];
28
+ if (index === reviewedSubjectArgument)
29
+ return reviewedSubject ?? "unknown";
14
30
  return typeof exactBinding === "string" && exactBinding.trim().length > 0
15
31
  ? exactBinding.trim()
16
32
  : (inferredArgs[index] ?? "unknown");
@@ -25,30 +41,59 @@ export function buildSuggestion(schema, text, subject, score, argumentBindings =
25
41
  const conformance = predicateArgumentConformance(schema.argument_names, boundArgs, vocabulary);
26
42
  const predicateArgs = applyArgumentRewrites(boundArgs, conformance.rewrites);
27
43
  const undeclaredArguments = new Set(conformance.undeclared.map((value) => value.argumentName));
44
+ // A schema without a `subject` argument may still lead with the hinted
45
+ // subject (inference put it there): that value is the agent's reviewed
46
+ // value, even when the schema names its first argument after it.
47
+ const hintedFirstArgument = explicitSubject &&
48
+ subjectArgument < 0 &&
49
+ !hasExactBinding(schema.argument_names[0] ?? "") &&
50
+ predicateArgs[0] === subject
51
+ ? 0
52
+ : -1;
28
53
  const bindingProvenanceByArgument = Object.fromEntries(schema.argument_names.map((name, index) => [
29
54
  name,
30
- classifyBinding(predicateArgs[index] ?? "unknown", text, (typeof argumentBindings[name] === "string" &&
31
- argumentBindings[name].trim().length > 0) ||
32
- (diagnostics?.explicitSubject === true && index === 0), canonicalLauncherArgs?.[index] === predicateArgs[index] &&
33
- !(index === 0 &&
34
- predicateArgs[index] === "launcher" &&
35
- diagnostics?.explicitSubject !== true &&
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
- }),
55
+ index === reviewedSubjectArgument && !undeclaredArguments.has(name)
56
+ ? explicitSubject
57
+ ? classifyBinding(predicateArgs[index] ?? "unknown", text, true)
58
+ : "requirement"
59
+ : classifyBinding(predicateArgs[index] ?? "unknown", text, hasExactBinding(name) || index === hintedFirstArgument, canonicalLauncherArgs?.[index] === predicateArgs[index] &&
60
+ !(index === 0 &&
61
+ predicateArgs[index] === "launcher" &&
62
+ !explicitSubject &&
63
+ typeof argumentBindings[name] !== "string"), index === hintedFirstArgument
64
+ ? { constants: schema.argument_constants?.[name] }
65
+ : {
66
+ argumentName: name,
67
+ argumentNames: schema.argument_names,
68
+ argumentType: schema.argument_types[index],
69
+ constants: schema.argument_constants?.[name],
70
+ }),
48
71
  ]));
72
+ // The subject the planned predicate fact is about, when the requirement
73
+ // constrains subjects: the `subject` argument's value, or for a schema
74
+ // without one the reviewed subject, recorded as the fact's subject_key.
75
+ const constrainedSubjects = diagnostics?.constrainedSubjects ?? [];
76
+ const subjectKey = constrainedSubjects.length === 0
77
+ ? null
78
+ : subjectArgument >= 0
79
+ ? (predicateArgs[subjectArgument] ?? null)
80
+ : (reviewedSubject ?? null);
81
+ const subjectPairing = constrainedSubjects.length === 0
82
+ ? "not_required"
83
+ : subjectKey !== null && constrainedSubjects.includes(subjectKey)
84
+ ? "paired"
85
+ : "unpaired";
49
86
  const bindingProvenance = aggregateBindingProvenance(Object.values(bindingProvenanceByArgument));
50
87
  const unboundArguments = schema.argument_names.filter((name) => undeclaredArguments.has(name) ||
51
88
  !bindingCanBeApplied(bindingProvenanceByArgument[name] ?? "placeholder"));
89
+ // An unpaired predicate would leave kb_check's strict-req-fact-pairing on
90
+ // the requirement, so the subject still needs a binding: the `subject`
91
+ // argument, or the fact's subject_key for a schema without one.
92
+ if (subjectPairing === "unpaired") {
93
+ const missing = subjectArgument >= 0 ? "subject" : "subject_key";
94
+ if (!unboundArguments.includes(missing))
95
+ unboundArguments.push(missing);
96
+ }
52
97
  const canonicalKey = `${schema.predicate_name}(${predicateArgs.join(",")})`;
53
98
  // Permission-style inference carries the deontic decision as its final
54
99
  // argument. Preserve that polarity in the typed suggestion instead of
@@ -68,6 +113,8 @@ export function buildSuggestion(schema, text, subject, score, argumentBindings =
68
113
  unbound_arguments: unboundArguments,
69
114
  binding_provenance: bindingProvenance,
70
115
  binding_provenance_by_argument: bindingProvenanceByArgument,
116
+ subject_key: subjectKey,
117
+ subject_pairing: subjectPairing,
71
118
  eligibility: diagnostics?.eligibility ?? "eligible",
72
119
  rejection_reasons: [...(diagnostics?.rejectionReasons ?? [])],
73
120
  applicability_score: diagnostics?.applicabilityScore ?? score,
@@ -86,7 +133,7 @@ export function buildSuggestion(schema, text, subject, score, argumentBindings =
86
133
  schema: schemaForCandidate(schema),
87
134
  };
88
135
  }
89
- // implements REQ-mcp-suggest-predicates
136
+ // implements REQ-mcp-suggest-predicates, REQ-model-predicates-requirement-subject-v2
90
137
  export function buildPredicateApplyPlan(suggestion, args) {
91
138
  if (suggestion.binding_status !== "complete" ||
92
139
  suggestion.eligibility !== "eligible")
@@ -112,6 +159,11 @@ export function buildPredicateApplyPlan(suggestion, args) {
112
159
  ...suggestion.schema.tags.map((tag) => `predicate:${tag}`),
113
160
  ],
114
161
  fact_kind: "predicate",
162
+ // The subject the predicate is about, so kb_check pairs it with the
163
+ // requirement's subject fact whatever argument holds the subject.
164
+ ...(suggestion.subject_key
165
+ ? { subject_key: suggestion.subject_key }
166
+ : {}),
115
167
  predicate_name: suggestion.predicate_name,
116
168
  predicate_args: suggestion.predicate_args,
117
169
  canonical_key: suggestion.canonical_key,
@@ -5,7 +5,7 @@ import type { BindingHint, PredicateSuggestion } from "./predicate-types.js";
5
5
  * current value was not accepted, so the agent binds from the claim text
6
6
  * instead of guessing.
7
7
  */
8
- export declare function buildBindingHints(candidate: PredicateSuggestion, text: string): BindingHint[];
8
+ export declare function buildBindingHints(candidate: PredicateSuggestion, text: string, requirementSubjects?: readonly string[]): BindingHint[];
9
9
  /** One line per hint for the text summary: `name (type, e.g. a, b)`. */
10
10
  export declare function describeBindingHints(hints: readonly BindingHint[]): string;
11
11
  //# sourceMappingURL=predicate-binding-hints.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-binding-hints.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-binding-hints.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAoD7E;;;;;GAKG;AAEH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,mBAAmB,EAC9B,IAAI,EAAE,MAAM,GACX,WAAW,EAAE,CA2Bf;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAW1E"}
1
+ {"version":3,"file":"predicate-binding-hints.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-binding-hints.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AA0D7E;;;;;GAKG;AAEH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,mBAAmB,EAC9B,IAAI,EAAE,MAAM,EACZ,mBAAmB,GAAE,SAAS,MAAM,EAAO,GAC1C,WAAW,EAAE,CA6Df;AAED,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAW1E"}
@@ -1,4 +1,4 @@
1
- import { isGenericPlaceholder, nameOrStopWordReason, } from "./predicate-bindings.js";
1
+ import { bindingCanBeApplied, clauseBindingReason, isGenericPlaceholder, nameOrStopWordReason, } from "./predicate-bindings.js";
2
2
  const MAX_EXAMPLES = 5;
3
3
  /** Split `name(a, b(c), d)` into its top-level arguments, or [] when it is not a call. */
4
4
  function exampleArguments(example) {
@@ -36,6 +36,12 @@ function unboundReason(candidate, name, value, text) {
36
36
  });
37
37
  if (nameReason !== null)
38
38
  return `The value "${value}" is not a binding: ${nameReason}.`;
39
+ const clauseReason = clauseBindingReason(value, {
40
+ argumentType: schema.argument_types[schema.argument_names.indexOf(name)],
41
+ constants: schema.argument_constants?.[name],
42
+ });
43
+ if (clauseReason !== null)
44
+ return `The value "${value}" is not a binding: ${clauseReason}. Bind a noun of at most 3 words, or the requirement's subject key.`;
39
45
  if (!value.trim() || isGenericPlaceholder(value))
40
46
  return "The claim text names no value for this argument.";
41
47
  if (provenance === "inferred")
@@ -50,17 +56,43 @@ function unboundReason(candidate, name, value, text) {
50
56
  * current value was not accepted, so the agent binds from the claim text
51
57
  * instead of guessing.
52
58
  */
53
- // implements REQ-model-predicates-binding-placeholders
54
- export function buildBindingHints(candidate, text) {
59
+ // implements REQ-model-predicates-binding-placeholders, REQ-model-predicates-requirement-subject-v2, REQ-model-predicates-binding-clauses
60
+ export function buildBindingHints(candidate, text, requirementSubjects = []) {
55
61
  const schema = candidate.schema;
56
62
  const parsedExamples = schema.examples.map(exampleArguments);
63
+ const namesSubject = schema.argument_names.includes("subject");
57
64
  return candidate.unbound_arguments.map((name) => {
65
+ // A schema without a subject argument records the requirement's subject
66
+ // as the predicate fact's subject_key instead.
67
+ if (name === "subject_key" && !namesSubject)
68
+ return {
69
+ argument: name,
70
+ position: -1,
71
+ type: "subject_key",
72
+ description: "The subject the predicate fact is about; recorded as its subject_key because the schema names no subject argument.",
73
+ examples: requirementSubjects.slice(0, MAX_EXAMPLES),
74
+ currentValue: candidate.subject_key ?? "",
75
+ provenance: "placeholder",
76
+ reason: `The ${schema.predicate_name} schema does not name the requirement's subject, so kb_check pairs the predicate with the subject fact only through subject_key. Pass subjectHint with one of the subjects the requirement constrains.`,
77
+ };
58
78
  const position = schema.argument_names.indexOf(name);
59
79
  const constants = schema.argument_constants?.[name];
60
80
  const exampleValues = parsedExamples
61
81
  .map((args) => args[position])
62
82
  .filter((value) => typeof value === "string");
63
- const examples = Array.from(new Set([...(constants ?? []), ...exampleValues])).slice(0, MAX_EXAMPLES);
83
+ // The subject keys the requirement already constrains come first, so
84
+ // the predicate and the subject fact use one identifier for the subject.
85
+ const examples = Array.from(new Set([
86
+ ...(name === "subject" ||
87
+ (!namesSubject &&
88
+ clauseBindingReason(candidate.predicate_args[position] ?? "", {
89
+ argumentType: schema.argument_types[position],
90
+ }) !== null)
91
+ ? requirementSubjects
92
+ : []),
93
+ ...(constants ?? []),
94
+ ...exampleValues,
95
+ ])).slice(0, MAX_EXAMPLES);
64
96
  const current = candidate.predicate_args[position] ?? "";
65
97
  const description = schema.argument_descriptions?.[position];
66
98
  return {
@@ -72,7 +104,11 @@ export function buildBindingHints(candidate, text) {
72
104
  examples,
73
105
  currentValue: current,
74
106
  provenance: candidate.binding_provenance_by_argument[name] ?? "placeholder",
75
- reason: unboundReason(candidate, name, current, text),
107
+ reason: name === "subject" &&
108
+ candidate.subject_pairing === "unpaired" &&
109
+ bindingCanBeApplied(candidate.binding_provenance_by_argument[name] ?? "placeholder")
110
+ ? `The value "${current}" is not a subject the requirement constrains (${requirementSubjects.join(", ")}), so kb_check would not pair the predicate with the subject fact. Bind one of them.`
111
+ : unboundReason(candidate, name, current, text),
76
112
  };
77
113
  });
78
114
  }
@@ -10,6 +10,8 @@ export type BindingContext = Readonly<{
10
10
  argumentNames?: readonly string[];
11
11
  /** Declared constants of this argument: always valid when matched. */
12
12
  constants?: readonly string[] | undefined;
13
+ /** The argument's declared type (`entity`, `actor`, `resource`, ...). */
14
+ argumentType?: string | undefined;
13
15
  /**
14
16
  * The claim text. A value that repeats an argument name is still a real
15
17
  * binding when the claim itself names it (a `launcher` argument bound to
@@ -23,6 +25,17 @@ export type BindingContext = Readonly<{
23
25
  * word. Declared constants and the booleans `true`/`false` always pass.
24
26
  */
25
27
  export declare function nameOrStopWordReason(value: string, context?: BindingContext): string | null;
28
+ /** The most words a value of a naming argument may have. */
29
+ export declare const MAX_NAMING_BINDING_WORDS = 3;
30
+ /**
31
+ * Why a value taken from the claim text cannot name a participant, or null
32
+ * when it can: a value of an entity-, actor- or resource-like argument with
33
+ * more than MAX_NAMING_BINDING_WORDS words is a clause of the claim (for
34
+ * example everything before the modal verb), not the participant's name.
35
+ * Explicit bindings, requirement subjects and declared constants are not
36
+ * judged here.
37
+ */
38
+ export declare function clauseBindingReason(value: string, context?: BindingContext): string | null;
26
39
  export declare function classifyBinding(value: string, text: string, explicit: boolean, canonical?: boolean, context?: BindingContext): BindingProvenance;
27
40
  export declare function aggregateBindingProvenance(values: readonly BindingProvenance[]): BindingProvenance;
28
41
  export declare function bindingCanBeApplied(value: BindingProvenance): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-bindings.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-bindings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAkG9D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAW3D;AAgED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,sDAAsD;AACtD,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,sEAAsE;IACtE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC1C;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC,CAAC;AAiBH;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,cAAmB,GAC3B,MAAM,GAAG,IAAI,CAYf;AAGD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,SAAS,UAAQ,EACjB,OAAO,GAAE,cAAmB,GAC3B,iBAAiB,CAmBnB;AASD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,iBAAiB,EAAE,GACnC,iBAAiB,CAQnB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAErE"}
1
+ {"version":3,"file":"predicate-bindings.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-bindings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAgH9D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAW3D;AAgED,iEAAiE;AACjE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,sDAAsD;AACtD,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC;IACpC,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,sEAAsE;IACtE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC1C,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC,CAAC;AAiBH;;;;GAIG;AAEH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,cAAmB,GAC3B,MAAM,GAAG,IAAI,CAYf;AAgBD,4DAA4D;AAC5D,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C;;;;;;;GAOG;AAEH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,cAAmB,GAC3B,MAAM,GAAG,IAAI,CAOf;AAGD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,SAAS,UAAQ,EACjB,OAAO,GAAE,cAAmB,GAC3B,iBAAiB,CAsBnB;AAUD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,iBAAiB,EAAE,GACnC,iBAAiB,CAQnB;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAIrE"}
@@ -67,6 +67,20 @@ const DERIVED_CUE_ALIASES = {
67
67
  ],
68
68
  actionable_error: ["actionable", "clear error", "report", "error"],
69
69
  exception: ["exception", "except", "package-manager", "package manager"],
70
+ // Wording that names a closed-vocabulary constant without spelling it.
71
+ deny: [
72
+ "denied",
73
+ "must not",
74
+ "must never",
75
+ "forbidden",
76
+ "prohibited",
77
+ "not allowed",
78
+ "not be allowed",
79
+ ],
80
+ allow: ["is allowed", "are allowed", "permitted"],
81
+ on_demand: ["on demand", "on request"],
82
+ timeout: ["times out", "timed out", "time out"],
83
+ submit: ["submission", "submits", "submitted"],
70
84
  };
71
85
  function textContainsValue(text, value) {
72
86
  const lower = text.toLowerCase();
@@ -195,7 +209,42 @@ export function nameOrStopWordReason(value, context = {}) {
195
209
  return `it repeats the argument name ${token} and the claim does not name it`;
196
210
  return null;
197
211
  }
198
- // implements REQ-mcp-suggest-predicates, REQ-model-predicates-binding-placeholders
212
+ /**
213
+ * Argument types that name a participant (who or what), so a value is a
214
+ * short noun or a subject key, never a clause of the claim.
215
+ */
216
+ const NAMING_ARGUMENT_TYPES = new Set([
217
+ "entity",
218
+ "actor",
219
+ "actor_scope",
220
+ "resource",
221
+ "owner",
222
+ "role",
223
+ "component",
224
+ ]);
225
+ /** The most words a value of a naming argument may have. */
226
+ export const MAX_NAMING_BINDING_WORDS = 3;
227
+ /**
228
+ * Why a value taken from the claim text cannot name a participant, or null
229
+ * when it can: a value of an entity-, actor- or resource-like argument with
230
+ * more than MAX_NAMING_BINDING_WORDS words is a clause of the claim (for
231
+ * example everything before the modal verb), not the participant's name.
232
+ * Explicit bindings, requirement subjects and declared constants are not
233
+ * judged here.
234
+ */
235
+ // implements REQ-model-predicates-binding-clauses
236
+ export function clauseBindingReason(value, context = {}) {
237
+ const type = context.argumentType ? bindingToken(context.argumentType) : "";
238
+ if (!NAMING_ARGUMENT_TYPES.has(type))
239
+ return null;
240
+ if (matchesConstant(value, context))
241
+ return null;
242
+ const words = bindingToken(value).split("_").filter(Boolean);
243
+ if (words.length <= MAX_NAMING_BINDING_WORDS)
244
+ return null;
245
+ return `it is a ${words.length}-word clause of the claim, not a short name for this ${type}`;
246
+ }
247
+ // implements REQ-mcp-suggest-predicates, REQ-model-predicates-binding-placeholders, REQ-model-predicates-binding-clauses
199
248
  export function classifyBinding(value, text, explicit, canonical = false, context = {}) {
200
249
  const normalized = value.trim();
201
250
  // A value that only repeats an argument name or a stop word is unbound
@@ -212,12 +261,17 @@ export function classifyBinding(value, text, explicit, canonical = false, contex
212
261
  return "explicit";
213
262
  if (!normalized || isGenericPlaceholder(normalized))
214
263
  return "placeholder";
264
+ // A clause lifted from the claim names no participant: it stays unbound
265
+ // like a placeholder so the agent binds a short noun or the subject key.
266
+ if (clauseBindingReason(normalized, context) !== null)
267
+ return "placeholder";
215
268
  if (textContainsValue(text, normalized))
216
269
  return "extracted";
217
270
  return "inferred";
218
271
  }
219
272
  const PROVENANCE_ORDER = [
220
273
  "explicit",
274
+ "requirement",
221
275
  "extracted",
222
276
  "inferred",
223
277
  "placeholder",
@@ -228,5 +282,5 @@ export function aggregateBindingProvenance(values) {
228
282
  : worst, "explicit");
229
283
  }
230
284
  export function bindingCanBeApplied(value) {
231
- return value === "explicit" || value === "extracted";
285
+ return (value === "explicit" || value === "requirement" || value === "extracted");
232
286
  }
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-catalog-1.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-1.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAmNzD,CAAC"}
1
+ {"version":3,"file":"predicate-catalog-1.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-1.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAqNzD,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { LIFECYCLE_ACTION_VOCABULARY, TRIGGER_VOCABULARY, closedArguments, } from "./predicate-closed-vocabularies.js";
1
2
  // implements REQ-mcp-suggest-predicates
2
3
  export const PREDICATE_CATALOG_1 = [
3
4
  {
@@ -18,6 +19,7 @@ export const PREDICATE_CATALOG_1 = [
18
19
  description: "A subject transitions between states because of a trigger.",
19
20
  argument_names: ["subject", "from_state", "to_state", "trigger"],
20
21
  argument_types: ["entity", "state", "state", "trigger"],
22
+ ...closedArguments({ trigger: TRIGGER_VOCABULARY }),
21
23
  keywords: [
22
24
  "transition",
23
25
  "enter",
@@ -193,6 +195,7 @@ export const PREDICATE_CATALOG_1 = [
193
195
  description: "An entity must be archived, deleted, expired, or lifecycle-transitioned after a duration.",
194
196
  argument_names: ["subject", "action", "duration", "unit"],
195
197
  argument_types: ["entity", "action", "number", "unit"],
198
+ ...closedArguments({ action: LIFECYCLE_ACTION_VOCABULARY }),
196
199
  keywords: ["archive", "archived", "delete", "deleted", "expire", "after"],
197
200
  examples: ["lifecycle_rule(expired_sessions, archived, 30, days)"],
198
201
  tags: ["lifecycle", "retention"],
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-catalog-2.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-2.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAkOzD,CAAC"}
1
+ {"version":3,"file":"predicate-catalog-2.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-2.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAoOzD,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { CODING_STANDARD_ACTION_VOCABULARY, ENVIRONMENT_DECISION_VOCABULARY, closedArguments, } from "./predicate-closed-vocabularies.js";
1
2
  // implements REQ-mcp-suggest-predicates
2
3
  export const PREDICATE_CATALOG_2 = [
3
4
  {
@@ -66,6 +67,7 @@ export const PREDICATE_CATALOG_2 = [
66
67
  description: "A named action is allowed or forbidden in a deployment environment.",
67
68
  argument_names: ["action", "decision", "environment"],
68
69
  argument_types: ["action", "decision", "environment"],
70
+ ...closedArguments({ decision: ENVIRONMENT_DECISION_VOCABULARY }),
69
71
  keywords: ["production", "staging", "forbidden", "destructive"],
70
72
  examples: [
71
73
  "environment_safety_rule(destructive_operations, forbidden, production)",
@@ -90,6 +92,7 @@ export const PREDICATE_CATALOG_2 = [
90
92
  description: "Developer-facing code must use or avoid a framework API, pattern, or documentation practice.",
91
93
  argument_names: ["subject", "action", "target"],
92
94
  argument_types: ["entity", "action", "api"],
95
+ ...closedArguments({ action: CODING_STANDARD_ACTION_VOCABULARY }),
93
96
  keywords: ["computed", "signals", "must use", "must not use"],
94
97
  examples: ["coding_standard_rule(derived_state, use, computed_signals)"],
95
98
  tags: ["coding-standard", "agent-guidance"],
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-catalog-3.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-3.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EA0NzD,CAAC"}
1
+ {"version":3,"file":"predicate-catalog-3.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-3.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAgOzD,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { DECISION_VOCABULARY, OPERATOR_VOCABULARY, REFRESH_POLICY_VOCABULARY, RETENTION_UNIT_VOCABULARY, TRIGGER_VOCABULARY, closedArguments, } from "./predicate-closed-vocabularies.js";
1
2
  // implements REQ-mcp-suggest-predicates
2
3
  export const PREDICATE_CATALOG_3 = [
3
4
  {
@@ -20,6 +21,7 @@ export const PREDICATE_CATALOG_3 = [
20
21
  description: "A subject refreshes a target automatically or by a named policy.",
21
22
  argument_names: ["subject", "target", "policy"],
22
23
  argument_types: ["entity", "target", "policy"],
24
+ ...closedArguments({ policy: REFRESH_POLICY_VOCABULARY }),
23
25
  keywords: ["automatically refresh", "manual page reload", "refresh"],
24
26
  examples: ["refresh_policy_rule(dashboard, processing_videos, automatic)"],
25
27
  tags: ["refresh", "ui"],
@@ -31,6 +33,7 @@ export const PREDICATE_CATALOG_3 = [
31
33
  description: "A scoped actor is allowed or denied an action because of assignment, ownership, or membership.",
32
34
  argument_names: ["actor_scope", "action", "decision"],
33
35
  argument_types: ["actor_scope", "action", "decision"],
36
+ ...closedArguments({ decision: DECISION_VOCABULARY }),
34
37
  keywords: ["unassigned", "assigned", "denied", "scoped"],
35
38
  examples: [
36
39
  "scoped_authorization_rule(unassigned_instructors, signed_url_generation, deny)",
@@ -131,6 +134,7 @@ export const PREDICATE_CATALOG_3 = [
131
134
  description: "A trigger commits, saves, or persists a subject within a scope.",
132
135
  argument_names: ["subject", "trigger", "scope"],
133
136
  argument_types: ["entity", "trigger", "scope"],
137
+ ...closedArguments({ trigger: TRIGGER_VOCABULARY }),
134
138
  keywords: [
135
139
  "save",
136
140
  "saves",
@@ -153,6 +157,7 @@ export const PREDICATE_CATALOG_3 = [
153
157
  description: "A trigger discards or cancels changes for a subject within a scope.",
154
158
  argument_names: ["subject", "trigger", "scope"],
155
159
  argument_types: ["entity", "trigger", "scope"],
160
+ ...closedArguments({ trigger: TRIGGER_VOCABULARY }),
156
161
  keywords: ["discard", "cancel", "escape", "revert", "without save"],
157
162
  examples: ["discard_action(editor.annotation, escape, active_annotation)"],
158
163
  tags: ["persistence", "workflow"],
@@ -175,6 +180,7 @@ export const PREDICATE_CATALOG_3 = [
175
180
  description: "A subject is retained for a bounded duration.",
176
181
  argument_names: ["subject", "duration", "unit"],
177
182
  argument_types: ["entity", "number", "unit"],
183
+ ...closedArguments({ unit: RETENTION_UNIT_VOCABULARY }),
178
184
  keywords: ["retain", "retained", "retention", "days", "months", "years"],
179
185
  examples: ["retention_policy(customer.data, 7, years)"],
180
186
  tags: ["data", "policy"],
@@ -186,6 +192,7 @@ export const PREDICATE_CATALOG_3 = [
186
192
  description: "A subject constrains a resource by operator, threshold, and unit.",
187
193
  argument_names: ["subject", "resource", "operator", "threshold", "unit"],
188
194
  argument_types: ["entity", "resource", "operator", "number", "unit"],
195
+ ...closedArguments({ operator: OPERATOR_VOCABULARY }),
189
196
  keywords: [
190
197
  "limit",
191
198
  "maximum",
@@ -1 +1 @@
1
- {"version":3,"file":"predicate-catalog-4.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-4.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAoTzD,CAAC"}
1
+ {"version":3,"file":"predicate-catalog-4.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-catalog-4.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AAGrE,eAAO,MAAM,mBAAmB,EAAE,wBAAwB,EAqTzD,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { DECISION_VOCABULARY, closedArguments, } from "./predicate-closed-vocabularies.js";
1
2
  // implements REQ-mcp-suggest-predicates
2
3
  export const PREDICATE_CATALOG_4 = [
3
4
  {
@@ -201,6 +202,7 @@ export const PREDICATE_CATALOG_4 = [
201
202
  description: "An actor is allowed or denied an action against a resource.",
202
203
  argument_names: ["actor", "action", "resource", "decision"],
203
204
  argument_types: ["actor", "action", "resource", "decision"],
205
+ ...closedArguments({ decision: DECISION_VOCABULARY }),
204
206
  keywords: [
205
207
  "may",
206
208
  "can",
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Closed argument vocabularies shared by built-in predicate schemas. An
3
+ * argument with a natural closed vocabulary declares its constants, so
4
+ * kb_model binds a constant the claim names (or an alias of it) and lists the
5
+ * constants as `allowedValues` when the claim names none, instead of minting
6
+ * a new atom per requirement.
7
+ */
8
+ /** UI and workflow events that start a commit, discard or transition. */
9
+ export declare const TRIGGER_VOCABULARY: {
10
+ readonly constants: readonly ["escape", "cancel", "submit", "navigation", "click", "timeout"];
11
+ readonly aliases: {
12
+ readonly esc: "escape";
13
+ readonly escape_key: "escape";
14
+ readonly cancellation: "cancel";
15
+ readonly submission: "submit";
16
+ readonly navigate: "navigation";
17
+ readonly navigates: "navigation";
18
+ readonly navigating: "navigation";
19
+ readonly navigation_away: "navigation";
20
+ readonly clicks: "click";
21
+ readonly time_out: "timeout";
22
+ readonly timed_out: "timeout";
23
+ };
24
+ };
25
+ /** An authorization outcome. */
26
+ export declare const DECISION_VOCABULARY: {
27
+ readonly constants: readonly ["allow", "deny"];
28
+ readonly aliases: {
29
+ readonly assert: "allow";
30
+ readonly allowed: "allow";
31
+ readonly permit: "allow";
32
+ readonly permitted: "allow";
33
+ readonly denied: "deny";
34
+ readonly forbid: "deny";
35
+ readonly forbidden: "deny";
36
+ };
37
+ };
38
+ /** How a target is refreshed. */
39
+ export declare const REFRESH_POLICY_VOCABULARY: {
40
+ readonly constants: readonly ["automatic", "manual", "on_demand"];
41
+ readonly aliases: {
42
+ readonly auto: "automatic";
43
+ readonly automatically: "automatic";
44
+ readonly manually: "manual";
45
+ readonly "on-demand": "on_demand";
46
+ };
47
+ };
48
+ /** What an environment safety rule decides for an action. */
49
+ export declare const ENVIRONMENT_DECISION_VOCABULARY: {
50
+ readonly constants: readonly ["allowed", "forbidden", "read-only"];
51
+ readonly aliases: {
52
+ readonly allow: "allowed";
53
+ readonly deny: "forbidden";
54
+ readonly denied: "forbidden";
55
+ readonly read_only: "read-only";
56
+ readonly readonly: "read-only";
57
+ };
58
+ };
59
+ /** Comparison operators, as property_value facts spell them. */
60
+ export declare const OPERATOR_VOCABULARY: {
61
+ readonly constants: readonly ["lt", "lte", "eq", "neq", "gte", "gt"];
62
+ readonly aliases: {
63
+ readonly le: "lte";
64
+ readonly ge: "gte";
65
+ readonly ne: "neq";
66
+ };
67
+ };
68
+ /** Calendar units a retention period is stated in. */
69
+ export declare const RETENTION_UNIT_VOCABULARY: {
70
+ readonly constants: readonly ["days", "months", "years"];
71
+ readonly aliases: {
72
+ readonly day: "days";
73
+ readonly month: "months";
74
+ readonly year: "years";
75
+ };
76
+ };
77
+ /** The two directions of a coding standard. */
78
+ export declare const CODING_STANDARD_ACTION_VOCABULARY: {
79
+ readonly constants: readonly ["use", "avoid"];
80
+ readonly aliases: {};
81
+ };
82
+ /** What happens to a subject at the end of its lifecycle. */
83
+ export declare const LIFECYCLE_ACTION_VOCABULARY: {
84
+ readonly constants: readonly ["archived", "deleted", "expired"];
85
+ readonly aliases: {
86
+ readonly archive: "archived";
87
+ readonly delete: "deleted";
88
+ readonly expire: "expired";
89
+ };
90
+ };
91
+ type Vocabulary = Readonly<{
92
+ constants: readonly string[];
93
+ aliases: Readonly<Record<string, string>>;
94
+ }>;
95
+ /**
96
+ * The `argument_constants` and `argument_aliases` fields for a schema whose
97
+ * listed arguments use the given vocabularies.
98
+ */
99
+ export declare function closedArguments(byArgument: Readonly<Record<string, Vocabulary>>): {
100
+ argument_constants: Record<string, string[]>;
101
+ argument_aliases?: Record<string, Record<string, string>>;
102
+ };
103
+ export {};
104
+ //# sourceMappingURL=predicate-closed-vocabularies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicate-closed-vocabularies.d.ts","sourceRoot":"","sources":["../../../src/operations/modeling/predicate-closed-vocabularies.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,yEAAyE;AAEzE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;CAerB,CAAC;AAEX,gCAAgC;AAEhC,eAAO,MAAM,mBAAmB;;;;;;;;;;;CAYtB,CAAC;AAEX,iCAAiC;AAEjC,eAAO,MAAM,yBAAyB;;;;;;;;CAQ5B,CAAC;AAEX,6DAA6D;AAE7D,eAAO,MAAM,+BAA+B;;;;;;;;;CASlC,CAAC;AAEX,gEAAgE;AAEhE,eAAO,MAAM,mBAAmB;;;;;;;CAGtB,CAAC;AAEX,sDAAsD;AAEtD,eAAO,MAAM,yBAAyB;;;;;;;CAG5B,CAAC;AAEX,+CAA+C;AAE/C,eAAO,MAAM,iCAAiC;;;CAGpC,CAAC;AAEX,6DAA6D;AAE7D,eAAO,MAAM,2BAA2B;;;;;;;CAG9B,CAAC;AAEX,KAAK,UAAU,GAAG,QAAQ,CAAC;IACzB,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC3C,CAAC,CAAC;AAEH;;;GAGG;AAEH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAC/C;IACD,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC3D,CAWA"}