@aifabrix/builder 2.54.1 → 2.55.2

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 (253) hide show
  1. package/README.md +8 -0
  2. package/lib/agent-kit/apply-ops.js +82 -0
  3. package/lib/agent-kit/checksums.js +28 -0
  4. package/lib/agent-kit/classify.js +113 -0
  5. package/lib/agent-kit/claude-shim.js +61 -0
  6. package/lib/agent-kit/constants.js +55 -0
  7. package/lib/agent-kit/file-list.js +103 -0
  8. package/lib/agent-kit/host-approve.js +172 -0
  9. package/lib/agent-kit/hosting/github.js +63 -0
  10. package/lib/agent-kit/hosts.js +71 -0
  11. package/lib/agent-kit/init.js +319 -0
  12. package/lib/agent-kit/kit-root.js +17 -0
  13. package/lib/agent-kit/managed-section.js +79 -0
  14. package/lib/agent-kit/package-load.js +45 -0
  15. package/lib/agent-kit/plan-ops.js +69 -0
  16. package/lib/agent-kit/remote-prompts.js +272 -0
  17. package/lib/agent-kit/render-host-markdown.js +40 -0
  18. package/lib/agent-kit/repo-guard.js +47 -0
  19. package/lib/agent-kit/report.js +107 -0
  20. package/lib/agent-kit/run.js +82 -0
  21. package/lib/agent-kit/scm/git.js +122 -0
  22. package/lib/agent-kit/start.js +310 -0
  23. package/lib/agent-kit/state-file.js +83 -0
  24. package/lib/agent-kit/status.js +50 -0
  25. package/lib/agent-kit/v1-detect.js +81 -0
  26. package/lib/agent-kit/validate-kit.js +87 -0
  27. package/lib/agent-kit/workspace-name.js +40 -0
  28. package/lib/agent-kit/workspace-skeleton.js +103 -0
  29. package/lib/api/role-assistant-test-pack.api.js +86 -0
  30. package/lib/api/work-search.api.js +31 -4
  31. package/lib/api/work.api.js +19 -12
  32. package/lib/app/deploy.js +7 -4
  33. package/lib/app/list-external-systems.js +2 -1
  34. package/lib/app/offer-create-from-template.js +184 -0
  35. package/lib/app/show-display.js +33 -6
  36. package/lib/app/show.js +25 -2
  37. package/lib/channels/add-channel.js +1 -0
  38. package/lib/channels/approval-guides/claude.js +2 -2
  39. package/lib/channels/channel-add-failure.js +1 -0
  40. package/lib/channels/channel-add-remaining.js +15 -9
  41. package/lib/channels/channel-definition-status.js +115 -0
  42. package/lib/channels/channel-description.js +2 -1
  43. package/lib/channels/channel-install-envelope.js +4 -2
  44. package/lib/channels/channel-integration-generator.js +2 -0
  45. package/lib/channels/channel-runtime-resolver.js +14 -0
  46. package/lib/channels/channel-targets.js +31 -5
  47. package/lib/channels/claude-setup-values.js +4 -1
  48. package/lib/channels/slack-channel-add.js +59 -6
  49. package/lib/channels/slack-mcp-callback.js +51 -0
  50. package/lib/channels/slack-mcp-runtime.js +156 -0
  51. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  52. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  53. package/lib/cli/index.js +4 -0
  54. package/lib/cli/setup-app.js +8 -1
  55. package/lib/cli/setup-auth.js +11 -2
  56. package/lib/cli/setup-certification-commands.js +2 -2
  57. package/lib/cli/setup-certification.help.js +3 -1
  58. package/lib/commands/agent-kit-help.js +149 -0
  59. package/lib/commands/agent-kit.js +417 -0
  60. package/lib/commands/auth-status-user.js +99 -0
  61. package/lib/commands/auth-status.js +14 -4
  62. package/lib/commands/channel-add-help.js +5 -5
  63. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  64. package/lib/commands/datasource-load-export-cli.js +7 -2
  65. package/lib/commands/datasource-test-trust-cli.js +5 -1
  66. package/lib/commands/governance-command-action.js +5 -4
  67. package/lib/commands/identity-membership-role-cache.js +6 -1
  68. package/lib/commands/lifecycle-command-action.js +16 -15
  69. package/lib/commands/login-credentials.js +8 -3
  70. package/lib/commands/login-device.js +14 -5
  71. package/lib/commands/login.js +14 -1
  72. package/lib/commands/logout.js +50 -15
  73. package/lib/commands/repair-auth-apply.js +1 -1
  74. package/lib/commands/repair-datasource-key-skip.js +39 -0
  75. package/lib/commands/repair-datasource-keys.js +24 -12
  76. package/lib/commands/repair-rbac.js +16 -10
  77. package/lib/commands/role-assistant-copy-output.js +61 -0
  78. package/lib/commands/role-assistant-copy.js +155 -0
  79. package/lib/commands/role-assistant-test-output.js +8 -9
  80. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  81. package/lib/commands/role-assistant.js +140 -59
  82. package/lib/commands/search.js +143 -0
  83. package/lib/commands/test-trust-external.js +7 -5
  84. package/lib/commands/upload-sidecar-followups.js +4 -1
  85. package/lib/commands/verify-governance-command-action.js +9 -7
  86. package/lib/commands/verify-operations-command-action.js +9 -8
  87. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  88. package/lib/commands/verify-operations-step-plan.js +6 -33
  89. package/lib/commands/verify-operations-steps.js +3 -1
  90. package/lib/commands/verify-trust-command-action.js +6 -5
  91. package/lib/datasource/capability/relate-operations.js +28 -10
  92. package/lib/datasource/capability/run-capability-relate.js +48 -25
  93. package/lib/datasource/datasource-identifier.js +13 -5
  94. package/lib/deployment/deployer-poll-run.js +117 -35
  95. package/lib/external-system/deploy-dataplane-published.js +70 -0
  96. package/lib/external-system/deploy.js +12 -2
  97. package/lib/external-system/download-role-assistant.js +41 -15
  98. package/lib/external-system/download.js +8 -3
  99. package/lib/generator/helpers.js +12 -1
  100. package/lib/generator/wizard-datasource-key.js +54 -0
  101. package/lib/generator/wizard.js +55 -54
  102. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  103. package/lib/integration-definition/apply-relationships.js +195 -0
  104. package/lib/integration-definition/apply-static-enterprise.js +232 -0
  105. package/lib/integration-definition/apply-system-file.js +71 -0
  106. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  107. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  108. package/lib/integration-definition/apply.js +78 -59
  109. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  110. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  111. package/lib/lifecycle/role-assistant-governance.js +56 -21
  112. package/lib/programmatic/capability-registry.js +1 -0
  113. package/lib/programmatic/index.js +2 -0
  114. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  115. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  116. package/lib/programmatic/openapi-component-schemas.js +1 -0
  117. package/lib/programmatic/openapi-descriptions.js +9 -3
  118. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  119. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  120. package/lib/programmatic/openapi-request-schemas.js +5 -0
  121. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  122. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  123. package/lib/programmatic/openapi-spec.js +1 -0
  124. package/lib/programmatic/request-timeout.js +3 -0
  125. package/lib/programmatic/route-handler-map.js +7 -0
  126. package/lib/programmatic/run-channel-catalog.js +50 -8
  127. package/lib/programmatic/run-channel-install-options.js +5 -1
  128. package/lib/programmatic/run-download.js +41 -0
  129. package/lib/protection/auth-context.js +3 -1
  130. package/lib/protection/protection-display.js +3 -2
  131. package/lib/protection/report-exit.js +50 -0
  132. package/lib/protection/run-commands.js +2 -0
  133. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  134. package/lib/role-assistant/knowledge-sync.js +9 -126
  135. package/lib/role-assistant/package-validate.js +175 -0
  136. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  137. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  138. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  139. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  140. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  141. package/lib/role-assistant/test-case-copy.js +119 -0
  142. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  143. package/lib/role-assistant/test-case-validate.js +88 -0
  144. package/lib/role-assistant/test-cases-bid.js +117 -4
  145. package/lib/role-assistant/test-cases-evaluate.js +149 -31
  146. package/lib/role-assistant/test-cases-evidence.js +44 -5
  147. package/lib/role-assistant/test-cases-honesty.js +155 -6
  148. package/lib/role-assistant/test-cases-lifecycle.js +297 -0
  149. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  150. package/lib/role-assistant/test-cases-search.js +20 -2
  151. package/lib/role-assistant/test-cases.js +34 -168
  152. package/lib/role-assistant/test-pack-map.js +180 -0
  153. package/lib/role-assistant/test-pack-sync.js +187 -0
  154. package/lib/role-assistant/test-runner-debug.js +54 -0
  155. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  156. package/lib/role-assistant/test-runner-search.js +20 -5
  157. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  158. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  159. package/lib/role-assistant/test-runner-workhub.js +45 -21
  160. package/lib/role-assistant/test-runner.js +53 -10
  161. package/lib/schema/external-datasource.schema.json +36 -5
  162. package/lib/schema/external-system.schema.json +20 -2
  163. package/lib/schema/infra-parameter.schema.json +51 -3
  164. package/lib/schema/infra.parameter.yaml +476 -40
  165. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  166. package/lib/schema/type/document-storage.json +1 -1
  167. package/lib/search/dataplane-context.js +23 -0
  168. package/lib/search/search-args.js +211 -0
  169. package/lib/search/search-display.js +238 -0
  170. package/lib/search/search-errors.js +50 -0
  171. package/lib/search/search-prompts.js +119 -0
  172. package/lib/search/search-roles.js +85 -0
  173. package/lib/search/search-run.js +241 -0
  174. package/lib/search/search-skills.js +198 -0
  175. package/lib/utils/api-required-permissions.js +271 -0
  176. package/lib/utils/api.js +16 -13
  177. package/lib/utils/authentication-slot-show.js +153 -0
  178. package/lib/utils/cli-json-exit.js +41 -0
  179. package/lib/utils/cli-utils.js +23 -5
  180. package/lib/utils/controller-url.js +78 -4
  181. package/lib/utils/credential-display.js +43 -1
  182. package/lib/utils/deployment-errors.js +15 -0
  183. package/lib/utils/deployment-validation-helpers.js +4 -1
  184. package/lib/utils/device-code.js +31 -10
  185. package/lib/utils/error-formatters/error-parser.js +16 -1
  186. package/lib/utils/error-formatters/permission-errors.js +19 -5
  187. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  188. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  189. package/lib/utils/external-system-validators.js +2 -1
  190. package/lib/utils/help-builder.js +3 -1
  191. package/lib/utils/keycloak-realm-ready.js +5 -2
  192. package/lib/utils/logger.js +1 -0
  193. package/lib/utils/paths.js +26 -5
  194. package/lib/utils/schema-loader.js +37 -0
  195. package/lib/utils/secrets-helpers.js +2 -2
  196. package/lib/utils/write-stream-and-wait.js +63 -0
  197. package/lib/validation/validate-external-file.js +5 -1
  198. package/lib/validation/validate.js +3 -2
  199. package/package.json +20 -6
  200. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  201. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  202. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  203. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  204. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  205. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  206. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  207. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  208. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  209. package/templates/agent-kit/agent-kit.yaml +45 -0
  210. package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
  211. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  212. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  213. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  214. package/templates/agent-kit/parity-inventory.yaml +188 -0
  215. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
  216. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  217. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  218. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  219. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  220. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
  221. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  222. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  223. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  224. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
  225. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  226. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  227. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  228. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
  229. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  230. package/templates/agent-kit/skills/shared/hosts.md +65 -0
  231. package/templates/agent-kit/skills/shared/login.md +16 -0
  232. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  233. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  234. package/templates/agent-kit/workspace/README.md +89 -0
  235. package/templates/applications/builder-api/Dockerfile +3 -1
  236. package/templates/applications/dataplane/application.yaml +2 -3
  237. package/templates/applications/dataplane/env.template +5 -5
  238. package/templates/applications/dataplane/rbac.yaml +28 -0
  239. package/templates/applications/keycloak/env.template +3 -3
  240. package/templates/applications/miso-controller/application.yaml +21 -2
  241. package/templates/applications/miso-controller/env.template +96 -25
  242. package/templates/applications/miso-controller/rbac.yaml +5 -0
  243. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  244. package/templates/channels/claude/channel.json +1 -1
  245. package/templates/channels/claude/install.template.md.hbs +1 -0
  246. package/templates/channels/claude/integration/claude-system.json +1 -1
  247. package/templates/channels/slack/channel.json +11 -0
  248. package/templates/channels/slack/install.template.md.hbs +9 -0
  249. package/templates/channels/slack/integration/slack-system.json +11 -0
  250. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  251. package/templates/channels/teams-bot/channel.json +1 -0
  252. package/templates/python/docker-compose.hbs +14 -0
  253. package/templates/typescript/docker-compose.hbs +14 -0
@@ -4,6 +4,8 @@
4
4
 
5
5
  'use strict';
6
6
 
7
+ const { skipRow, notesRow } = require('./test-case-assertion-row');
8
+
7
9
  const HONESTY_COMPLETION_STATES = new Set([
8
10
  'notEvaluated',
9
11
  'waiting',
@@ -26,6 +28,14 @@ const HONESTY_CONTRIB_STATUSES = new Set([
26
28
  'notRealized',
27
29
  'partial'
28
30
  ]);
31
+ const OBJECTIVE_TEXT_KEYS = new Set([
32
+ 'objective',
33
+ 'goal',
34
+ 'planObjective',
35
+ 'objectiveSummary',
36
+ 'acceptedObjective'
37
+ ]);
38
+ const FORBIDDEN_BUILDER_TOKEN = /\b(?:knownPlatform|gold-package|[a-z][a-z0-9-]*\.[a-z][a-zA-Z0-9]+)\b/i;
29
39
 
30
40
  function emptyHonestyObservation() {
31
41
  return {
@@ -83,7 +93,59 @@ function pushHonestyAssertion(assertions, id, passed, message, options = {}) {
83
93
  assertions.push({ id, passed, message });
84
94
  }
85
95
 
86
- function assertWorkResultPresent(assertions, honesty, observation) {
96
+ function collectObjectiveStrings(value, acc) {
97
+ if (Array.isArray(value)) {
98
+ value.forEach((item) => collectObjectiveStrings(item, acc));
99
+ return;
100
+ }
101
+ if (!value || typeof value !== 'object') {
102
+ return;
103
+ }
104
+ for (const [key, child] of Object.entries(value)) {
105
+ if (OBJECTIVE_TEXT_KEYS.has(key) && typeof child === 'string') {
106
+ acc.push(child);
107
+ } else if (child && typeof child === 'object') {
108
+ collectObjectiveStrings(child, acc);
109
+ }
110
+ }
111
+ }
112
+
113
+ function interactionObjectiveText(row) {
114
+ if (!row || typeof row !== 'object') {
115
+ return '';
116
+ }
117
+ const role = String(row.role || row.type || '').toLowerCase();
118
+ if (role === 'user' || role === 'human') {
119
+ return '';
120
+ }
121
+ return [row.message, row.content, row.text, row.objective]
122
+ .filter((item) => typeof item === 'string')
123
+ .join('\n');
124
+ }
125
+
126
+ function governedObjectiveHaystack(observation) {
127
+ const parts = [];
128
+ collectObjectiveStrings(observation.workResult || {}, parts);
129
+ collectObjectiveStrings(observation.emittedBid || {}, parts);
130
+ for (const row of Array.isArray(observation.interactions) ? observation.interactions : []) {
131
+ const text = interactionObjectiveText(row);
132
+ if (text) {
133
+ parts.push(text);
134
+ }
135
+ }
136
+ return parts.join('\n');
137
+ }
138
+
139
+ function assertWorkResultPresent(assertions, honestySpec, honesty, observation, soft) {
140
+ if (honestySpec.requireWorkResult === false) {
141
+ assertions.push(
142
+ skipRow(
143
+ 'expected.honesty.requireWorkResult',
144
+ 'requireWorkResult is false — work.result is not required.'
145
+ )
146
+ );
147
+ return;
148
+ }
87
149
  const present = honesty.present === true;
88
150
  const missingSuffix = observation.workResultError
89
151
  ? `: ${observation.workResultError}`
@@ -94,7 +156,8 @@ function assertWorkResultPresent(assertions, honesty, observation) {
94
156
  present,
95
157
  present
96
158
  ? 'work.result Output was present.'
97
- : `work.result Output missing${missingSuffix}`
159
+ : `work.result Output missing${missingSuffix}`,
160
+ { soft }
98
161
  );
99
162
  }
100
163
 
@@ -102,6 +165,15 @@ function assertSchemaVersion(assertions, honestySpec, honesty, soft) {
102
165
  if (!honestySpec.schemaVersion) {
103
166
  return;
104
167
  }
168
+ if (!honesty.present) {
169
+ assertions.push(
170
+ skipRow(
171
+ 'expected.honesty.schemaVersion',
172
+ 'schemaVersion skipped because work.result is absent.'
173
+ )
174
+ );
175
+ return;
176
+ }
105
177
  const expected = String(honestySpec.schemaVersion);
106
178
  const passed = String(honesty.schemaVersion || '') === expected;
107
179
  pushHonestyAssertion(
@@ -117,6 +189,12 @@ function assertSchemaVersion(assertions, honestySpec, honesty, soft) {
117
189
 
118
190
  function assertCompletion(assertions, honestySpec, honesty, soft) {
119
191
  if (honestySpec.requireCompletion === false) {
192
+ assertions.push(
193
+ skipRow(
194
+ 'expected.honesty.requireCompletion',
195
+ 'requireCompletion is false — completion check skipped.'
196
+ )
197
+ );
120
198
  return;
121
199
  }
122
200
  const stateOk = HONESTY_COMPLETION_STATES.has(String(honesty.completionState || ''));
@@ -134,6 +212,12 @@ function assertCompletion(assertions, honestySpec, honesty, soft) {
134
212
 
135
213
  function assertContribution(assertions, honestySpec, honesty, soft) {
136
214
  if (honestySpec.requireContributionObservation === false) {
215
+ assertions.push(
216
+ skipRow(
217
+ 'expected.honesty.requireContributionObservation',
218
+ 'requireContributionObservation is false — contribution check skipped.'
219
+ )
220
+ );
137
221
  return;
138
222
  }
139
223
  const ok = HONESTY_CONTRIB_STATUSES.has(String(honesty.contributionStatus || ''));
@@ -149,9 +233,28 @@ function assertContribution(assertions, honestySpec, honesty, soft) {
149
233
  }
150
234
 
151
235
  function assertMinResourceChanges(assertions, honestySpec, honesty, soft) {
236
+ if (typeof honestySpec.softResourceChanges === 'boolean') {
237
+ assertions.push(
238
+ notesRow(
239
+ 'expected.honesty.softResourceChanges',
240
+ honestySpec.softResourceChanges
241
+ ? 'softResourceChanges: true — minResourceChanges misses are warnings.'
242
+ : 'softResourceChanges: false — minResourceChanges misses are blocking.'
243
+ )
244
+ );
245
+ }
152
246
  if (typeof honestySpec.minResourceChanges !== 'number') {
153
247
  return;
154
248
  }
249
+ if (!honesty.present) {
250
+ assertions.push(
251
+ skipRow(
252
+ 'expected.honesty.minResourceChanges',
253
+ 'minResourceChanges skipped because work.result is absent.'
254
+ )
255
+ );
256
+ return;
257
+ }
155
258
  const count = Number(honesty.resourceChangeCount || 0);
156
259
  const passed = count >= honestySpec.minResourceChanges;
157
260
  const softChange = soft || honestySpec.softResourceChanges === true;
@@ -171,6 +274,53 @@ function assertMinResourceChanges(assertions, honestySpec, honesty, soft) {
171
274
  }
172
275
  }
173
276
 
277
+ function assertForbidBuilderKeys(assertions, honestySpec, observation, soft) {
278
+ if (honestySpec.forbidBuilderKeysInObjective === false) {
279
+ assertions.push(
280
+ skipRow(
281
+ 'expected.honesty.forbidBuilderKeysInObjective',
282
+ 'forbidBuilderKeysInObjective is false — Builder-key check skipped.'
283
+ )
284
+ );
285
+ return;
286
+ }
287
+ if (honestySpec.forbidBuilderKeysInObjective !== true) {
288
+ return;
289
+ }
290
+ const haystack = governedObjectiveHaystack(observation);
291
+ const match = haystack.match(FORBIDDEN_BUILDER_TOKEN);
292
+ const passed = !match;
293
+ pushHonestyAssertion(
294
+ assertions,
295
+ 'expected.honesty.forbidBuilderKeysInObjective',
296
+ passed,
297
+ passed
298
+ ? 'Objective text has no Builder capability keys or generation tokens.'
299
+ : `Objective text contains forbidden token "${match[0]}".`,
300
+ { soft }
301
+ );
302
+ }
303
+
304
+ function emitPresentFlagRows(assertions, honestySpec, honesty, observation, soft) {
305
+ if (honestySpec.requireCompletion === false || honestySpec.requireCompletion === true) {
306
+ assertCompletion(assertions, honestySpec, honesty, soft);
307
+ }
308
+ if (
309
+ honestySpec.requireContributionObservation === false ||
310
+ honestySpec.requireContributionObservation === true
311
+ ) {
312
+ assertContribution(assertions, honestySpec, honesty, soft);
313
+ }
314
+ if (
315
+ typeof honestySpec.minResourceChanges === 'number' ||
316
+ typeof honestySpec.softResourceChanges === 'boolean'
317
+ ) {
318
+ assertMinResourceChanges(assertions, honestySpec, honesty, soft);
319
+ }
320
+ assertSchemaVersion(assertions, honestySpec, honesty, soft);
321
+ assertForbidBuilderKeys(assertions, honestySpec, observation, soft);
322
+ }
323
+
174
324
  function evaluateHonestyExpectations(honestySpec, observation, options = {}) {
175
325
  if (!honestySpec || typeof honestySpec !== 'object' || Array.isArray(honestySpec)) {
176
326
  return [];
@@ -178,17 +328,16 @@ function evaluateHonestyExpectations(honestySpec, observation, options = {}) {
178
328
  const soft = options.soft === true;
179
329
  const honesty = observation.honesty || {};
180
330
  const assertions = [];
181
- if (honestySpec.requireWorkResult !== false) {
182
- assertWorkResultPresent(assertions, honesty, observation);
183
- }
331
+ assertWorkResultPresent(assertions, honestySpec, honesty, observation, soft);
184
332
  if (!honesty.present) {
185
- // Missing Output is always a hard failure when required — soft does not waive it.
333
+ emitPresentFlagRows(assertions, honestySpec, honesty, observation, soft);
186
334
  return assertions;
187
335
  }
188
336
  assertSchemaVersion(assertions, honestySpec, honesty, soft);
189
337
  assertCompletion(assertions, honestySpec, honesty, soft);
190
338
  assertContribution(assertions, honestySpec, honesty, soft);
191
339
  assertMinResourceChanges(assertions, honestySpec, honesty, soft);
340
+ assertForbidBuilderKeys(assertions, honestySpec, observation, soft);
192
341
  return assertions;
193
342
  }
194
343
 
@@ -0,0 +1,297 @@
1
+ /**
2
+ * Map role-assistant test results to expected lifecycleState tokens.
3
+ * @fileoverview Prefer work.result completion over coarse work.status.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { VERDICT } = require('../lifecycle/product-model');
9
+
10
+ const SUPPORTED_LIFECYCLE_STATES = new Set([
11
+ 'completed',
12
+ 'question',
13
+ 'approval-required',
14
+ 'dependency',
15
+ 'safe-stop',
16
+ 'rejected',
17
+ 'failed'
18
+ ]);
19
+ const LIFECYCLE_STATE_ALIASES = Object.freeze({
20
+ completed: ['completed', 'complete', 'done', 'success', 'verified'],
21
+ question: ['question', 'question_required', 'awaiting_question', 'pending_question'],
22
+ 'approval-required': [
23
+ 'approval_required',
24
+ 'awaiting_approval',
25
+ 'pending_approval',
26
+ 'requires_approval'
27
+ ],
28
+ dependency: ['dependency', 'dependency_wait', 'dependency_blocked', 'awaiting_dependency'],
29
+ 'safe-stop': ['safe_stop', 'safe_stopped', 'policy_blocked', 'halted', 'paused'],
30
+ rejected: ['rejected', 'denied', 'declined', 'policy_rejected'],
31
+ failed: ['failed', 'error', 'errored', 'timeout', 'cancelled', 'canceled']
32
+ });
33
+ const WORK_STATUS_TO_LIFECYCLE = Object.freeze({
34
+ completed: 'completed',
35
+ waiting: 'question',
36
+ running: 'dependency',
37
+ stopped: 'safe-stop'
38
+ });
39
+ const WAIT_REASON_TO_LIFECYCLE = Object.freeze({
40
+ ask: 'question',
41
+ approval: 'approval-required',
42
+ ownerroute: 'dependency',
43
+ realityconfirm: 'question',
44
+ knowledgeconfirm: 'question'
45
+ });
46
+ const WORKER_TASK_STATUS_TO_LIFECYCLE = Object.freeze({
47
+ draft: 'dependency',
48
+ pendingapproval: 'approval-required',
49
+ approved: 'dependency',
50
+ running: 'dependency',
51
+ completed: 'completed',
52
+ failed: 'failed',
53
+ cancelled: 'rejected',
54
+ canceled: 'rejected'
55
+ });
56
+ const WORKER_APPROVAL_STATE_TO_LIFECYCLE = Object.freeze({
57
+ pending: 'approval-required',
58
+ approved: 'dependency',
59
+ rejected: 'rejected'
60
+ });
61
+ const COMPLETION_STATE_TO_LIFECYCLE = Object.freeze({
62
+ safestopped: 'safe-stop',
63
+ safe_stopped: 'safe-stop',
64
+ cancelled: 'rejected',
65
+ canceled: 'rejected',
66
+ completed: 'completed',
67
+ waiting: 'question'
68
+ });
69
+ const STEP_SIGNAL_FIELDS = [
70
+ 'lifecycleState',
71
+ 'state',
72
+ 'status',
73
+ 'code',
74
+ 'errorCode',
75
+ 'reason',
76
+ 'kind',
77
+ 'type',
78
+ 'error',
79
+ 'failure',
80
+ 'details',
81
+ 'result'
82
+ ];
83
+ const OBJECT_SIGNAL_FIELDS = [
84
+ 'lifecycleState',
85
+ 'state',
86
+ 'status',
87
+ 'code',
88
+ 'type',
89
+ 'reason',
90
+ 'kind',
91
+ 'category',
92
+ 'errorCode'
93
+ ];
94
+ const TOP_LEVEL_SIGNAL_PATHS = [
95
+ 'lifecycleState',
96
+ 'state',
97
+ 'status',
98
+ 'lifecycle.state',
99
+ 'report.lifecycleState',
100
+ 'report.state',
101
+ 'report.status',
102
+ 'report.lifecycle.state',
103
+ 'report.runtime.state',
104
+ 'report.work.state'
105
+ ];
106
+
107
+ function normalizeLifecycleStateToken(value) {
108
+ if (typeof value !== 'string') {
109
+ return '';
110
+ }
111
+ return value
112
+ .trim()
113
+ .toLowerCase()
114
+ .replace(/[^a-z0-9]+/g, '_')
115
+ .replace(/^_+|_+$/g, '');
116
+ }
117
+
118
+ function lifecycleStateFromToken(token) {
119
+ if (!token) {
120
+ return null;
121
+ }
122
+ for (const [state, aliases] of Object.entries(LIFECYCLE_STATE_ALIASES)) {
123
+ for (const alias of aliases) {
124
+ if (token === alias || token.startsWith(`${alias}_`)) {
125
+ return state;
126
+ }
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+
132
+ function lifecycleStateFromSignalValue(value) {
133
+ if (value === null || value === undefined) {
134
+ return null;
135
+ }
136
+ if (typeof value === 'string') {
137
+ return lifecycleStateFromToken(normalizeLifecycleStateToken(value));
138
+ }
139
+ if (Array.isArray(value)) {
140
+ return firstLifecycleFromValues(value);
141
+ }
142
+ if (typeof value === 'object') {
143
+ return firstLifecycleFromValues(OBJECT_SIGNAL_FIELDS.map((field) => value[field]));
144
+ }
145
+ return null;
146
+ }
147
+
148
+ function firstLifecycleFromValues(values) {
149
+ for (const candidate of values) {
150
+ const state = lifecycleStateFromSignalValue(candidate);
151
+ if (state) {
152
+ return state;
153
+ }
154
+ }
155
+ return null;
156
+ }
157
+
158
+ function mapWorkStatusFromResult(result) {
159
+ const rawStatus = normalizeLifecycleStateToken(result?.status);
160
+ if (rawStatus && WORK_STATUS_TO_LIFECYCLE[rawStatus]) {
161
+ const waitReason = normalizeLifecycleStateToken(result?.wait?.waitReason || '');
162
+ if (rawStatus === 'waiting' && waitReason && WAIT_REASON_TO_LIFECYCLE[waitReason]) {
163
+ return WAIT_REASON_TO_LIFECYCLE[waitReason];
164
+ }
165
+ return WORK_STATUS_TO_LIFECYCLE[rawStatus];
166
+ }
167
+ return null;
168
+ }
169
+
170
+ function mapWorkerTaskStateFromResult(result) {
171
+ const statusCandidates = [result?.task?.status, result?.status, result?.workerTask?.status];
172
+ for (const row of statusCandidates) {
173
+ const token = normalizeLifecycleStateToken(row);
174
+ if (token && WORKER_TASK_STATUS_TO_LIFECYCLE[token]) {
175
+ return WORKER_TASK_STATUS_TO_LIFECYCLE[token];
176
+ }
177
+ }
178
+ const approvalCandidates = [
179
+ result?.task?.approvalState,
180
+ result?.approvalState,
181
+ result?.workerTask?.approvalState
182
+ ];
183
+ for (const row of approvalCandidates) {
184
+ const token = normalizeLifecycleStateToken(row);
185
+ if (token && WORKER_APPROVAL_STATE_TO_LIFECYCLE[token]) {
186
+ return WORKER_APPROVAL_STATE_TO_LIFECYCLE[token];
187
+ }
188
+ }
189
+ return null;
190
+ }
191
+
192
+ function workResultCompletionObject(result) {
193
+ const workResult =
194
+ result?.workResult && typeof result.workResult === 'object' ? result.workResult : null;
195
+ const completion =
196
+ workResult?.completion && typeof workResult.completion === 'object'
197
+ ? workResult.completion
198
+ : null;
199
+ return { workResult, completion };
200
+ }
201
+
202
+ function mapWorkResultCompletionToLifecycle(result) {
203
+ const { workResult, completion } = workResultCompletionObject(result);
204
+ const state = normalizeLifecycleStateToken(completion?.state);
205
+ if (!workResult || !completion || !state) {
206
+ return null;
207
+ }
208
+ if (state === 'failed') {
209
+ return workResultSignalsRejected(workResult) ? 'rejected' : 'failed';
210
+ }
211
+ return COMPLETION_STATE_TO_LIFECYCLE[state] || lifecycleStateFromToken(state);
212
+ }
213
+
214
+ function completionReasonBag(processExecution) {
215
+ const pe = processExecution && typeof processExecution === 'object' ? processExecution : {};
216
+ if (pe.completionReason && typeof pe.completionReason === 'object') {
217
+ return pe.completionReason;
218
+ }
219
+ if (pe.completion_reason && typeof pe.completion_reason === 'object') {
220
+ return pe.completion_reason;
221
+ }
222
+ return {};
223
+ }
224
+
225
+ function workResultSignalsRejected(workResult) {
226
+ const reasonBag = completionReasonBag(workResult?.processExecution);
227
+ const kind = normalizeLifecycleStateToken(reasonBag.kind || reasonBag.Kind);
228
+ if (kind === 'rejected' || kind === 'denied' || kind === 'unauthorized') {
229
+ return true;
230
+ }
231
+ const text = [
232
+ reasonBag.summary,
233
+ workResult?.completion?.businessLanguage?.reason,
234
+ workResult?.completion?.businessLanguage?.summary,
235
+ workResult?.completion?.businessLanguage?.outcomeState,
236
+ workResult?.task?.resultSummary
237
+ ]
238
+ .filter((row) => typeof row === 'string' && row.trim())
239
+ .join(' ')
240
+ .toLowerCase();
241
+ return /\b(trust|authority|denied|rejected|unauthorized|not authorized)\b/.test(text);
242
+ }
243
+
244
+ function valueAtDottedPath(source, dottedPath) {
245
+ return dottedPath.split('.').reduce((cursor, key) => {
246
+ if (!cursor || typeof cursor !== 'object') {
247
+ return undefined;
248
+ }
249
+ return cursor[key];
250
+ }, source);
251
+ }
252
+
253
+ function lifecycleStateFromStepsRun(result) {
254
+ const steps = Array.isArray(result?.stepsRun) ? result.stepsRun : [];
255
+ for (const row of steps) {
256
+ const state = firstLifecycleFromValues(STEP_SIGNAL_FIELDS.map((field) => row?.[field]));
257
+ if (state) {
258
+ return state;
259
+ }
260
+ }
261
+ return null;
262
+ }
263
+
264
+ function resolveLifecycleStateFromResult(result) {
265
+ // Prefer governed work.result completion over coarse work.status "stopped"
266
+ // (which collapses failed + safeStopped + cancelled into safe-stop).
267
+ return (
268
+ mapWorkResultCompletionToLifecycle(result) ||
269
+ mapWorkStatusFromResult(result) ||
270
+ mapWorkerTaskStateFromResult(result) ||
271
+ firstLifecycleFromValues(TOP_LEVEL_SIGNAL_PATHS.map((path) => valueAtDottedPath(result, path))) ||
272
+ lifecycleStateFromStepsRun(result)
273
+ );
274
+ }
275
+
276
+ function deriveLifecycleState(result, lifecycleVerdict) {
277
+ // lifecycleVerdict is the Role Assistant's own operations/trust/governance
278
+ // certification status, not this case's execution outcome — a VERIFIED
279
+ // (published, trusted) Role Assistant can still run a case that fails,
280
+ // asks a question, or safe-stops. Always prefer a real structured signal
281
+ // from this execution's own result; the verdict is only a fallback default
282
+ // for when no signal exists at all (e.g. an empty/no-op result).
283
+ const structuredState = resolveLifecycleStateFromResult(result);
284
+ if (structuredState) {
285
+ return structuredState;
286
+ }
287
+ if (lifecycleVerdict === VERDICT.VERIFIED) {
288
+ return 'completed';
289
+ }
290
+ if (lifecycleVerdict === VERDICT.NOT_VERIFIED) return 'safe-stop';
291
+ return 'failed';
292
+ }
293
+
294
+ module.exports = {
295
+ SUPPORTED_LIFECYCLE_STATES,
296
+ deriveLifecycleState
297
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Graph hop assertions for expected.search (one-level related records).
3
+ *
4
+ * @fileoverview Evaluate graphDepth, relatedTypes, and related.minRows
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ function asObject(value) {
12
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
13
+ }
14
+
15
+ function relatedRows(payload, resourceType) {
16
+ const related = asObject(asObject(payload).related);
17
+ if (!Object.prototype.hasOwnProperty.call(related, resourceType)) {
18
+ return null;
19
+ }
20
+ const bundle = related[resourceType];
21
+ if (Array.isArray(bundle)) {
22
+ return bundle;
23
+ }
24
+ if (bundle && Array.isArray(bundle.data)) {
25
+ return bundle.data;
26
+ }
27
+ return [];
28
+ }
29
+
30
+ function relatedMeta(payload, resourceType) {
31
+ const bundle = asObject(asObject(payload).related)[resourceType];
32
+ return asObject(asObject(bundle).meta);
33
+ }
34
+
35
+ function evaluateGraphDepth(expectedSearch, meta) {
36
+ if (!Number.isFinite(Number(expectedSearch.graphDepth))) {
37
+ return null;
38
+ }
39
+ const expected = Number(expectedSearch.graphDepth);
40
+ const actual = Number(meta.graphDepth);
41
+ const passed = actual === expected;
42
+ return {
43
+ id: 'expected.search.graphDepth',
44
+ passed,
45
+ message: passed
46
+ ? `Search graphDepth matched ${expected}.`
47
+ : `Search graphDepth expected ${expected} but got ${Number.isFinite(actual) ? actual : 'none'}.`
48
+ };
49
+ }
50
+
51
+ function evaluateRelatedTypes(expectedTypes, meta) {
52
+ if (!Array.isArray(expectedTypes) || expectedTypes.length === 0) {
53
+ return [];
54
+ }
55
+ const observed = Array.isArray(meta.relatedTypes) ? meta.relatedTypes.map(String) : [];
56
+ return expectedTypes.map((item) => {
57
+ const key = String(item || '').trim();
58
+ const passed = Boolean(key && observed.includes(key));
59
+ return {
60
+ id: `expected.search.relatedTypes:${key}`,
61
+ passed,
62
+ message: passed
63
+ ? `Search relatedTypes included "${key}".`
64
+ : `Search relatedTypes did not include "${key}".`
65
+ };
66
+ });
67
+ }
68
+
69
+ function evaluateRelatedHop(resourceType, spec, payload) {
70
+ const assertions = [];
71
+ const rows = relatedRows(payload, resourceType);
72
+ if (Number.isFinite(Number(spec.minRows))) {
73
+ const minRows = Number(spec.minRows);
74
+ if (rows === null) {
75
+ assertions.push({
76
+ id: `expected.search.related.${resourceType}.minRows`,
77
+ passed: false,
78
+ message: `Related "${resourceType}" was omitted from the search graph.`
79
+ });
80
+ } else {
81
+ const passed = rows.length >= minRows;
82
+ assertions.push({
83
+ id: `expected.search.related.${resourceType}.minRows`,
84
+ passed,
85
+ message: passed
86
+ ? `Related "${resourceType}" returned at least ${minRows} row(s).`
87
+ : `Related "${resourceType}" returned ${rows.length} row(s); expected at least ${minRows}.`
88
+ });
89
+ }
90
+ }
91
+ if (spec.resultMode) {
92
+ const actual = String(relatedMeta(payload, resourceType).resultMode || '');
93
+ const passed = rows !== null && actual === String(spec.resultMode);
94
+ assertions.push({
95
+ id: `expected.search.related.${resourceType}.resultMode`,
96
+ passed,
97
+ message: passed
98
+ ? `Related "${resourceType}" resultMode matched "${spec.resultMode}".`
99
+ : `Related "${resourceType}" resultMode expected "${spec.resultMode}" but got "${actual || 'omitted'}".`
100
+ });
101
+ }
102
+ return assertions;
103
+ }
104
+
105
+ function evaluateSearchGraphExpectations(expectedSearch, payload, meta) {
106
+ const assertions = [];
107
+ const depth = evaluateGraphDepth(expectedSearch, meta);
108
+ if (depth) {
109
+ assertions.push(depth);
110
+ }
111
+ assertions.push(...evaluateRelatedTypes(expectedSearch.relatedTypes, meta));
112
+ const related = asObject(expectedSearch.related);
113
+ for (const resourceType of Object.keys(related)) {
114
+ const spec = asObject(related[resourceType]);
115
+ assertions.push(...evaluateRelatedHop(resourceType, spec, payload));
116
+ }
117
+ return assertions;
118
+ }
119
+
120
+ module.exports = {
121
+ evaluateSearchGraphExpectations
122
+ };
@@ -9,6 +9,8 @@
9
9
  'use strict';
10
10
 
11
11
  const { operationKeys } = require('./test-runner-search');
12
+ const { skillsEnvelope } = require('../search/search-skills');
13
+ const { evaluateSearchGraphExpectations } = require('./test-cases-search-graph');
12
14
 
13
15
  function asObject(value) {
14
16
  return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
@@ -128,11 +130,19 @@ function evaluateSearchExpectations(expectedSearch, observation) {
128
130
  assertions.push(errorAssertion);
129
131
  }
130
132
  assertions.push(...evaluateSearchShape(expectedSearch, searchObs));
133
+ assertions.push(
134
+ ...evaluateSearchGraphExpectations(
135
+ expectedSearch,
136
+ searchObs.payload,
137
+ searchMeta(searchObs.payload)
138
+ )
139
+ );
131
140
  return assertions;
132
141
  }
133
142
 
134
143
  function skillsResourceEntry(skillsPayload, resourceType) {
135
- const rts = asObject(asObject(skillsPayload).search).resourceTypes;
144
+ const root = skillsEnvelope(skillsPayload) || asObject(skillsPayload);
145
+ const rts = asObject(asObject(root).search).resourceTypes;
136
146
  return asObject(asObject(rts)[resourceType]);
137
147
  }
138
148
 
@@ -208,7 +218,15 @@ function evaluateSkillsExpectations(expectedSkills, observation) {
208
218
  : `work.skills did not publish resourceType "${resourceType}".`
209
219
  });
210
220
  }
211
- if (expectedSkills.operationRequired === true) {
221
+ if (expectedSkills.operationRequired === false) {
222
+ assertions.push({
223
+ id: 'expected.skills.operationRequired',
224
+ passed: true,
225
+ skip: true,
226
+ kind: 'skip',
227
+ message: 'operationRequired is false — check skipped.'
228
+ });
229
+ } else if (expectedSkills.operationRequired === true) {
212
230
  const passed = spec.operationRequired === true;
213
231
  assertions.push({
214
232
  id: 'expected.skills.operationRequired',