@aifabrix/builder 2.60.0 → 2.62.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 (177) hide show
  1. package/README.md +14 -11
  2. package/docs/README.md +80 -0
  3. package/docs/builder-help/evidence-patterns.json +155 -0
  4. package/docs/builder-help/golden-examples/crm-company.json +30 -0
  5. package/docs/builder-help/golden-examples/crm-deal.json +29 -0
  6. package/docs/builder-help/golden-examples/document-storage-keyed-get.json +85 -0
  7. package/docs/builder-help/golden-examples/document-storage.json +30 -0
  8. package/docs/builder-help/golden-examples/meeting-transcript.json +29 -0
  9. package/docs/builder-help/golden-examples/repository-template.json +29 -0
  10. package/docs/builder-help/golden-examples/service-ticket.json +29 -0
  11. package/docs/builder-help/platform-roles.json +98 -0
  12. package/docs/builder-help/resource-type-catalog.json +402 -0
  13. package/lib/api/applications.api.js +22 -2
  14. package/lib/api/bootstrap-snapshot.api.js +115 -0
  15. package/lib/api/configuration.api.js +131 -0
  16. package/lib/api/system-secrets.api.js +72 -0
  17. package/lib/app/deploy.js +14 -5
  18. package/lib/app/run-docker-fallback.js +6 -1
  19. package/lib/app/run-env-compose.js +5 -6
  20. package/lib/app/run-helpers.js +2 -1
  21. package/lib/app/run-parameter-sync.js +142 -0
  22. package/lib/app/run.js +6 -4
  23. package/lib/build/docker-build-args.js +5 -2
  24. package/lib/build/index.js +3 -2
  25. package/lib/build/standard-docker-build.js +6 -2
  26. package/lib/channels/channel-artifact-generator.js +13 -3
  27. package/lib/cli/infra-guided-footers.js +10 -8
  28. package/lib/cli/infra-guided.js +30 -10
  29. package/lib/cli/setup-app.js +5 -0
  30. package/lib/cli/setup-environment.js +156 -0
  31. package/lib/cli/setup-infra-up-platform-action.js +14 -1
  32. package/lib/cli/setup-platform.js +2 -0
  33. package/lib/commands/auth-config.js +2 -0
  34. package/lib/commands/datasource-capability-upsert-cli.js +112 -0
  35. package/lib/commands/datasource-capability.js +4 -2
  36. package/lib/commands/env-secret-context.js +113 -0
  37. package/lib/commands/env-secret-list.js +200 -0
  38. package/lib/commands/env-secret-push-confirm.js +54 -0
  39. package/lib/commands/env-secret-push-run.js +227 -0
  40. package/lib/commands/env-secret-push.js +168 -0
  41. package/lib/commands/login.js +4 -0
  42. package/lib/commands/repair-datasource-apply.js +5 -1
  43. package/lib/commands/repair-datasource-keyed-document.js +122 -0
  44. package/lib/commands/repair-datasource-legacy.js +94 -0
  45. package/lib/commands/repair-datasource-run.js +1 -0
  46. package/lib/commands/setup-image-refresh.js +26 -0
  47. package/lib/commands/setup-modes.js +8 -9
  48. package/lib/commands/setup-prompts-platform-mode.js +34 -0
  49. package/lib/commands/setup-prompts.js +2 -180
  50. package/lib/commands/setup.js +3 -0
  51. package/lib/commands/up-builder-api.js +24 -8
  52. package/lib/commands/up-common.js +16 -1
  53. package/lib/commands/up-dataplane-bootstrap.js +70 -14
  54. package/lib/commands/up-dataplane-credentials.js +21 -0
  55. package/lib/commands/up-dataplane.js +37 -18
  56. package/lib/commands/up-integration-server.js +112 -0
  57. package/lib/commands/verify-operations-skip-e2e.js +25 -1
  58. package/lib/commands/verify-operations-steps.js +13 -1
  59. package/lib/commands/wizard-config-normalizer.js +12 -5
  60. package/lib/commands/wizard-core.js +5 -157
  61. package/lib/commands/wizard-file-saving.js +163 -0
  62. package/lib/core/admin-secrets-env-overlay.js +8 -3
  63. package/lib/core/env-platform-expand.js +5 -1
  64. package/lib/core/environment-access-policy.js +121 -0
  65. package/lib/core/environment-mode-policy.js +177 -0
  66. package/lib/core/local-env-overrides.js +149 -0
  67. package/lib/core/secrets-ensure.js +4 -2
  68. package/lib/core/secrets-env-content.js +17 -10
  69. package/lib/core/secrets-env-write.js +11 -4
  70. package/lib/core/secrets-load.js +4 -2
  71. package/lib/datasource/binary-documents-validator.js +190 -0
  72. package/lib/datasource/capability/capability-manifest.js +1 -0
  73. package/lib/datasource/capability/run-capability-upsert.js +202 -0
  74. package/lib/datasource/capability/upsert-ingredients.js +291 -0
  75. package/lib/datasource/capability/upsert-operations.js +138 -0
  76. package/lib/datasource/capability/upsert-test-scaffold.js +189 -0
  77. package/lib/datasource/validate.js +12 -5
  78. package/lib/deployment/installation/index.js +14 -1
  79. package/lib/deployment/installation/infra-catalog.js +3 -0
  80. package/lib/deployment/installation/local-environment-stage.js +285 -0
  81. package/lib/deployment/installation/local-installation.js +76 -0
  82. package/lib/deployment/installation/stage-input.js +5 -1
  83. package/lib/generator/builders.js +3 -1
  84. package/lib/generator/index.js +3 -0
  85. package/lib/generator/split-variables.js +2 -2
  86. package/lib/lifecycle/product-model.js +4 -3
  87. package/lib/lifecycle/report-display.js +3 -2
  88. package/lib/parameters/database-adoption.js +125 -0
  89. package/lib/parameters/infra-parameter-catalog.js +1 -1
  90. package/lib/parameters/physical-database-naming.js +136 -0
  91. package/lib/parameters/physical-secret-name.js +194 -0
  92. package/lib/parameters/rsa-secret-values.js +165 -0
  93. package/lib/programmatic/builder-help-enterprise-sync-fabrix.js +1 -1
  94. package/lib/programmatic/builder-help-governance.js +1 -1
  95. package/lib/programmatic/builder-help.js +1 -1
  96. package/lib/programmatic/run-channel-install-artifacts.js +28 -2
  97. package/lib/programmatic/run-channel-package.js +3 -1
  98. package/lib/programmatic/workspace-context.js +37 -2
  99. package/lib/role-assistant/test-runner-workhub-answers.js +4 -1
  100. package/lib/role-assistant/test-runner-workhub-missing-fields.js +42 -0
  101. package/lib/role-assistant/test-runner-workhub-wait-stop.js +94 -0
  102. package/lib/role-assistant/test-runner-workhub.js +28 -19
  103. package/lib/schema/application-schema.json +14 -2
  104. package/lib/schema/external-datasource.schema.json +23 -3
  105. package/lib/schema/infra.parameter.yaml +440 -56
  106. package/lib/schema/infrastructure-schema.json +213 -120
  107. package/lib/utils/compose-generate-docker-compose.js +9 -14
  108. package/lib/utils/compose-generator.js +4 -7
  109. package/lib/utils/compose-miso-env.js +2 -2
  110. package/lib/utils/compose-traefik-ingress-base.js +3 -4
  111. package/lib/utils/datasource-binary-evidence.js +92 -0
  112. package/lib/utils/datasource-test-run-capability-scope.js +44 -1
  113. package/lib/utils/datasource-test-run-debug-display.js +2 -0
  114. package/lib/utils/datasource-test-run-display.js +8 -2
  115. package/lib/utils/datasource-test-run-issue-guidance.js +176 -0
  116. package/lib/utils/datasource-test-run-tty-log.js +2 -0
  117. package/lib/utils/declarative-url-ports.js +3 -0
  118. package/lib/utils/env-copy.js +11 -10
  119. package/lib/utils/env-environment-file-paths.js +105 -0
  120. package/lib/utils/environment-scoped-resources.js +48 -14
  121. package/lib/utils/external-system-system-test-tty.js +3 -2
  122. package/lib/utils/image-tags.js +2 -2
  123. package/lib/utils/paths-system-builder-keys.js +7 -1
  124. package/lib/utils/paths.js +7 -2
  125. package/lib/utils/platform-kv-ref.js +1 -1
  126. package/lib/utils/postgres-platform-bootstrap.js +35 -1
  127. package/lib/utils/prepare-local-data-mount.js +58 -0
  128. package/lib/utils/redis-env-scope.js +21 -1
  129. package/lib/utils/registry-auth-sources.js +154 -0
  130. package/lib/utils/registry-credentials.js +131 -14
  131. package/lib/utils/secrets-generator.js +30 -21
  132. package/lib/utils/secrets-helpers.js +5 -8
  133. package/lib/utils/secrets-kv-scope.js +127 -28
  134. package/lib/utils/secrets-materialize-local.js +9 -0
  135. package/lib/utils/secrets-missing-error.js +35 -1
  136. package/lib/utils/system-secret-mapping.js +125 -0
  137. package/lib/utils/test-log-writer.js +2 -1
  138. package/lib/utils/token-manager.js +14 -8
  139. package/lib/utils/url-public-path-prefix.js +6 -10
  140. package/lib/validation/external-manifest-validator.js +5 -0
  141. package/lib/validation/openapi-contract-surface-validator.js +3 -1
  142. package/lib/validation/validate-external-file.js +5 -1
  143. package/package.json +6 -4
  144. package/templates/agent-kit/agent-kit.yaml +1 -1
  145. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +2 -1
  146. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +15 -0
  147. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +3 -2
  148. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +6 -2
  149. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +22 -0
  150. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +7 -1
  151. package/templates/agent-kit/skills/aifabrix-role-assistant/references/testing-playbook.md +117 -0
  152. package/templates/agent-kit/skills/shared/interaction.md +83 -0
  153. package/templates/agent-kit/skills/shared/status.md +3 -12
  154. package/templates/applications/builder-api/application.yaml +1 -1
  155. package/templates/applications/builder-api/env.template +5 -1
  156. package/templates/applications/dataplane/application.yaml +2 -2
  157. package/templates/applications/dataplane/env.template +10 -12
  158. package/templates/applications/integration-server/application.yaml +91 -0
  159. package/templates/applications/integration-server/env.template +215 -0
  160. package/templates/applications/keycloak/application.yaml +6 -1
  161. package/templates/applications/mirrored-templates.json +52 -0
  162. package/templates/applications/miso-controller/application.yaml +116 -2
  163. package/templates/applications/miso-controller/env.template +62 -17
  164. package/templates/applications/miso-controller/rbac.yaml +17 -0
  165. package/templates/external-system/external-datasource.yaml.hbs +7 -1
  166. package/templates/marketplace/createUiDefinition.json +52 -4
  167. package/templates/marketplace/main.json +88 -10
  168. package/templates/python/docker-compose.hbs +1 -1
  169. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +0 -41
  170. /package/{lib/programmatic/help-content → docs/builder-help/content}/channel-onboarding.md +0 -0
  171. /package/{lib/programmatic/help-content → docs/builder-help/content}/cip-overview.md +0 -0
  172. /package/{lib/programmatic/help-content → docs/builder-help/content}/connected-system-ui.md +0 -0
  173. /package/{lib/programmatic/help-content → docs/builder-help/content}/dimensions-guide.md +0 -0
  174. /package/{lib/programmatic/help-content → docs/builder-help/content}/enterprise-sync-fabrix.md +0 -0
  175. /package/{lib/programmatic/help-content → docs/builder-help/content}/overview.md +0 -0
  176. /package/{lib/programmatic/help-content → docs/builder-help/content}/subscription-guide.md +0 -0
  177. /package/{lib/programmatic/help-content → docs/builder-help/content}/workflow.md +0 -0
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Map an application's `.env` variables onto Miso catalog keys.
3
+ *
4
+ * The `.env` is keyed by environment variable (`MORI_POLICY_PRIVATE_KEY`) while the system
5
+ * secrets API is keyed by catalog key (`secrets-policyPrivateKeyVault`). The mapping is not
6
+ * invented here: it is read from the application's own `env.template`, because the
7
+ * `NAME=kv://<catalogKey>` line is already the declaration that decides where the value is
8
+ * read from at runtime. A second table in the CLI would be free to drift from it.
9
+ *
10
+ * A variable whose template line is not a `kv://` reference is ordinary configuration, not a
11
+ * secret, and is skipped rather than pushed.
12
+ *
13
+ * Note this is the flat `kv://<catalogKey>` form used by the Controller's system secrets.
14
+ * The Dataplane's credential store uses `kv://<systemKey>/<path>`; see
15
+ * `credential-secrets-env-kv.js` for that one. They are not interchangeable.
16
+ *
17
+ * @fileoverview env.template kv:// declarations → catalog keys
18
+ * @author AI Fabrix Team
19
+ * @version 1.0.0
20
+ */
21
+
22
+ 'use strict';
23
+
24
+ /** `NAME=value`, ignoring blank lines, comments and `export ` prefixes. */
25
+ const ASSIGNMENT = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
26
+
27
+ /**
28
+ * Strip surrounding quotes and trailing whitespace from an env value.
29
+ * @param {string} raw - Raw right-hand side
30
+ * @returns {string} Cleaned value
31
+ */
32
+ function cleanValue(raw) {
33
+ const trimmed = String(raw === undefined || raw === null ? '' : raw).trim();
34
+ if (trimmed.length >= 2) {
35
+ const first = trimmed[0];
36
+ const last = trimmed[trimmed.length - 1];
37
+ if ((first === '"' && last === '"') || (first === '\'' && last === '\'')) {
38
+ return trimmed.slice(1, -1);
39
+ }
40
+ }
41
+ return trimmed;
42
+ }
43
+
44
+ /**
45
+ * Parse `NAME=value` lines into a Map, last assignment winning.
46
+ * @param {string} content - File content
47
+ * @returns {Map<string, string>} Variable name → value
48
+ */
49
+ function parseEnvAssignments(content) {
50
+ const out = new Map();
51
+ for (const line of String(content || '').split(/\r?\n/)) {
52
+ if (!line || /^\s*#/.test(line)) {
53
+ continue;
54
+ }
55
+ const match = line.match(ASSIGNMENT);
56
+ if (match) {
57
+ out.set(match[1], cleanValue(match[2]));
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+
63
+ /**
64
+ * Catalog keys declared by an `env.template`, keyed by environment variable name.
65
+ * Only `kv://` lines are secrets; everything else is skipped.
66
+ * @param {string} templateContent - env.template content
67
+ * @returns {Map<string, string>} Variable name → catalog key
68
+ */
69
+ function catalogKeysFromTemplate(templateContent) {
70
+ const out = new Map();
71
+ for (const [name, value] of parseEnvAssignments(templateContent)) {
72
+ if (!value.startsWith('kv://')) {
73
+ continue;
74
+ }
75
+ const catalogKey = value.slice('kv://'.length).trim();
76
+ if (catalogKey && !catalogKey.includes('/')) {
77
+ out.set(name, catalogKey);
78
+ }
79
+ }
80
+ return out;
81
+ }
82
+
83
+ /**
84
+ * Pair the values in a `.env` with the catalog keys its `env.template` declares.
85
+ *
86
+ * Skipped rows are returned rather than dropped, so the command can say why a variable was
87
+ * not pushed instead of silently ignoring it.
88
+ *
89
+ * @param {string} envContent - Operator's .env content
90
+ * @param {string} templateContent - The application's env.template content
91
+ * @param {Object} [options] - `only` restricts to one variable name
92
+ * @returns {{ pushable: Array<Object>, skipped: Array<Object> }} Rows carrying value and catalog key
93
+ */
94
+ function mapEnvToCatalogKeys(envContent, templateContent, options = {}) {
95
+ const declared = catalogKeysFromTemplate(templateContent);
96
+ const values = parseEnvAssignments(envContent);
97
+ const only = options.only ? String(options.only).trim() : '';
98
+ const pushable = [];
99
+ const skipped = [];
100
+ for (const [name, value] of values) {
101
+ if (only && name !== only) {
102
+ continue;
103
+ }
104
+ const catalogKey = declared.get(name);
105
+ if (!catalogKey) {
106
+ skipped.push({ name, reason: 'not a kv:// reference in env.template' });
107
+ continue;
108
+ }
109
+ if (!value) {
110
+ skipped.push({ name, catalogKey, reason: 'no value in the env file' });
111
+ continue;
112
+ }
113
+ pushable.push({ name, catalogKey, value });
114
+ }
115
+ if (only && pushable.length === 0 && skipped.length === 0) {
116
+ skipped.push({ name: only, reason: 'not present in the env file' });
117
+ }
118
+ return { pushable, skipped };
119
+ }
120
+
121
+ module.exports = {
122
+ parseEnvAssignments,
123
+ catalogKeysFromTemplate,
124
+ mapEnvToCatalogKeys
125
+ };
@@ -9,6 +9,7 @@
9
9
 
10
10
  const fs = require('fs').promises;
11
11
  const path = require('path');
12
+ const { sanitizeBinaryEvidence } = require('./datasource-binary-evidence');
12
13
 
13
14
  /**
14
15
  * Prepare object for JSON serialization (handles circular refs)
@@ -45,7 +46,7 @@ async function writeTestLog(appKey, data, logType = 'test-integration', integrat
45
46
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
46
47
  const filename = `${logType}-${timestamp}.json`;
47
48
  const filePath = path.join(logsDir, filename);
48
- const sanitized = sanitizeForLog(data);
49
+ const sanitized = sanitizeForLog(sanitizeBinaryEvidence(data));
49
50
  await fs.writeFile(filePath, JSON.stringify(sanitized, null, 2), 'utf8');
50
51
  return filePath;
51
52
  }
@@ -10,7 +10,6 @@
10
10
  */
11
11
 
12
12
  const config = require('../core/config');
13
- const logger = require('./logger');
14
13
  const { maskSensitiveData } = require('./log-redaction');
15
14
  const {
16
15
  refreshClientToken,
@@ -205,12 +204,17 @@ async function tryClientTokenAuth(environment, appName, controllerUrl) {
205
204
  return null;
206
205
  }
207
206
 
208
- function throwNoDeploymentAuth(credentialApp) {
209
- const err = new Error(
210
- `No authentication method available. For CI: set AIFABRIX_DEPLOYMENT_AUTH=client-credentials and MISO_CLIENTID/MISO_CLIENTSECRET in .env or '${credentialApp}-client-idKeyVault' in secrets.local.yaml. For interactive use: aifabrix login`
211
- );
207
+ // Two causes reach here: no credentials at all, or credentials the controller rejected.
208
+ // Saying "put it in secrets.local.yaml" for the second sends the reader after a key that is
209
+ // already present and simply not valid, so name the rejection instead.
210
+ function throwNoDeploymentAuth(credentialApp, exchangeError) {
211
+ const err = new Error(exchangeError
212
+ ? `Client credentials for '${credentialApp}' were rejected by the controller: ${exchangeError.message}. ` +
213
+ `Re-issue them with: aifabrix login && aifabrix app rotate-secret ${credentialApp}`
214
+ : `No authentication method available. For CI: set AIFABRIX_DEPLOYMENT_AUTH=client-credentials and MISO_CLIENTID/MISO_CLIENTSECRET in .env or '${credentialApp}-client-idKeyVault' in secrets.local.yaml. For interactive use: aifabrix login`);
212
215
  err.statusCode = 401;
213
216
  err.authFailure = true;
217
+ if (exchangeError) err.cause = exchangeError;
214
218
  throw err;
215
219
  }
216
220
 
@@ -262,8 +266,10 @@ async function getDeploymentAuth(controllerUrl, environment, appName, options =
262
266
  controller: controllerUrl
263
267
  };
264
268
  }
265
- } catch {
266
- // Refresh failed; fall through to throw below
269
+ } catch (err) {
270
+ // Keep it: credentials existed and the controller turned them down, which is a
271
+ // different problem from having none, and the only place that fact is observable.
272
+ throwNoDeploymentAuth(credentialApp, err);
267
273
  }
268
274
  }
269
275
 
@@ -341,7 +347,7 @@ async function forceRefreshDeviceToken(controllerUrl) {
341
347
 
342
348
  // Must have refresh token to force refresh
343
349
  if (!tokenInfo.refreshToken) {
344
- logger.warn('Cannot refresh: no refresh token available. Please login again using: aifabrix login');
350
+ warnRefreshFailureOnce(controllerUrl, 'Cannot refresh: no refresh token available. Please login again using: aifabrix login');
345
351
  return null;
346
352
  }
347
353
 
@@ -1,18 +1,20 @@
1
1
  /**
2
- * Plan 117 URL path prefix for url://public (dev/tst only when gated).
2
+ * Plan 117 URL path prefix for url://public (scoped environments only when gated).
3
3
  *
4
- * @fileoverview baseEffective ∧ derived envKey ∈ {dev,tst}. Caller must pass prefix only when `traefik` is on (see url-declarative-resolve).
4
+ * @fileoverview baseEffective ∧ derived envKey ∈ SCOPED_RUN_ENVIRONMENTS. Caller must pass prefix only when `traefik` is on (see url-declarative-resolve).
5
5
  * @author AI Fabrix Team
6
6
  * @version 1.0.0
7
7
  */
8
8
 
9
9
  'use strict';
10
10
 
11
+ const { isScopedRunEnvironmentKey } = require('./environment-scoped-resources');
12
+
11
13
  /**
12
14
  * @param {boolean} useEnvironmentScopedResources - config gate
13
15
  * @param {boolean} appEnvironmentScopedResources - application.yaml
14
16
  * @param {string} derivedEnvKey - from client id (dev|tst|pro|miso)
15
- * @returns {string} '' | '/dev' | '/tst'
17
+ * @returns {string} '' | '/dev' | '/tst' | '/pro'
16
18
  */
17
19
  function computePublicUrlPathPrefix(useEnvironmentScopedResources, appEnvironmentScopedResources, derivedEnvKey) {
18
20
  const baseEffective = Boolean(useEnvironmentScopedResources) && Boolean(appEnvironmentScopedResources);
@@ -20,13 +22,7 @@ function computePublicUrlPathPrefix(useEnvironmentScopedResources, appEnvironmen
20
22
  return '';
21
23
  }
22
24
  const k = String(derivedEnvKey || '').toLowerCase();
23
- if (k === 'dev') {
24
- return '/dev';
25
- }
26
- if (k === 'tst') {
27
- return '/tst';
28
- }
29
- return '';
25
+ return isScopedRunEnvironmentKey(k) ? `/${k}` : '';
30
26
  }
31
27
 
32
28
  module.exports = {
@@ -16,6 +16,7 @@ const path = require('path');
16
16
  const { formatValidationErrors } = require('../utils/error-formatter');
17
17
  const { validateFieldReferences } = require('../datasource/field-reference-validator');
18
18
  const { validateAbac } = require('../datasource/abac-validator');
19
+ const { validateBinaryDocumentsContract } = require('../datasource/binary-documents-validator');
19
20
  const { validateManifestRuntimeConfiguration } = require('./runtime-configuration-rules');
20
21
  const { validateAppContractSurface } = require('./openapi-contract-surface-validator');
21
22
 
@@ -147,9 +148,13 @@ function validateDatasources(manifest, ajv, externalDatasourceSchema, errors, wa
147
148
  } else {
148
149
  const fieldRefErrors = validateFieldReferences(datasource);
149
150
  const abacErrors = validateAbac(datasource);
151
+ const binaryDocuments = validateBinaryDocumentsContract(datasource);
150
152
  const prefix = `Datasource ${index + 1} (${datasource.key || 'unknown'}): `;
151
153
  fieldRefErrors.forEach(e => errors.push(prefix + e));
152
154
  abacErrors.forEach(e => errors.push(prefix + e));
155
+ // Codes are the dataplane's, so the offline answer matches the upload answer.
156
+ binaryDocuments.errors.forEach(e => errors.push(`${prefix}${e.code}: ${e.message}`));
157
+ binaryDocuments.warnings.forEach(w => warnings.push(`${prefix}${w.code}: ${w.message}`));
153
158
  }
154
159
  });
155
160
  }
@@ -426,5 +426,7 @@ module.exports = {
426
426
  validateDatasourceFileContractSurface,
427
427
  validateVendorOperationContract,
428
428
  validateServiceContractDatasourceContracts,
429
- findVendorOperation
429
+ findVendorOperation,
430
+ resolveVendorOpenApiSpec,
431
+ inferIntegrationContextFromPath
430
432
  };
@@ -111,11 +111,15 @@ function validateDatasourceFileExtras(parsed, filePath) {
111
111
  const { collectExternalDatasourceWarnings } = require('./datasource-warnings');
112
112
  const { validateDatasourceFileContractSurface } = require('./openapi-contract-surface-validator');
113
113
  const errors = [...validateFieldReferences(parsed), ...validateAbac(parsed)];
114
+ const { validateBinaryDocumentsContract } = require('../datasource/binary-documents-validator');
115
+ const binary = validateBinaryDocumentsContract(parsed);
116
+ errors.push(...binary.errors.map(issue => `${issue.code}: ${issue.message}`));
114
117
  const contractSurface = validateDatasourceFileContractSurface(parsed, filePath);
115
118
  errors.push(...contractSurface.errors);
116
119
  const warnings = [
117
120
  ...collectExternalDatasourceWarnings(parsed),
118
- ...contractSurface.warnings
121
+ ...contractSurface.warnings,
122
+ ...binary.warnings.map(issue => `${issue.code}: ${issue.message}`)
119
123
  ];
120
124
  return { errors, warnings };
121
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aifabrix/builder",
3
- "version": "2.60.0",
3
+ "version": "2.62.0",
4
4
  "description": "AI Fabrix Builder — CLI and developer scripts (pnpm + af)",
5
5
  "main": "lib/index.js",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "bin/aifabrix.js",
12
12
  "lib/",
13
+ "docs/builder-help/",
13
14
  "templates/",
14
15
  "README.md",
15
16
  "LICENSE"
@@ -24,7 +25,7 @@
24
25
  "dev-deploy": "node scripts/pnpm/dev-deploy.mjs",
25
26
  "af": "node scripts/pnpm/af.mjs",
26
27
  "check-quiet": "node scripts/pnpm/check-quiet.mjs",
27
- "agent-skills:sync": "node scripts/pnpm/agent-skills-sync.mjs",
28
+ "agent-skills:sync": "node scripts/pnpm/agent-skills-sync.mjs --codex-home",
28
29
  "agent-skills:check": "node scripts/pnpm/agent-skills-sync.mjs --check",
29
30
  "agent-skills:sync:codex": "node scripts/pnpm/agent-skills-sync.mjs --codex-home",
30
31
  "agent-skills:test": "node --test scripts/pnpm/agent-skills-sync.test.mjs",
@@ -34,11 +35,12 @@
34
35
  "lint:fix": "eslint . --ext .js --fix",
35
36
  "test": "node tests/scripts/test-wrapper.js",
36
37
  "build": "npm run lint && npm run test",
37
- "build:ci": "npm run lint && npm run check:schema-sync && npm run check:flags && npm run check:cli-help && npm run test:ci",
38
+ "build:ci": "npm run lint && npm run check:schema-sync && npm run check:flags && npm run check:cli-help && npm run check:mirrored-templates && npm run test:ci",
38
39
  "test:ci": "bash tests/scripts/ci-simulate.sh",
39
40
  "check:schema-sync": "node scripts/check-datasource-test-run-schema-sync.js",
40
41
  "check:flags": "jest tests/lib/schema/flag-map-validation-run.test.js --runInBand --config jest.config.default.js",
41
42
  "check:cli-help": "node scripts/check-cli-help-examples.js",
43
+ "check:mirrored-templates": "node scripts/check-mirrored-templates.js",
42
44
  "test:manual": "jest --config jest.config.manual.js --runInBand",
43
45
  "test:integration": "jest --config jest.config.integration.js --runInBand",
44
46
  "test:integration:fixtures": "jest --config jest.config.integration.fixtures.js --runInBand",
@@ -131,7 +133,7 @@
131
133
  }
132
134
  },
133
135
  "dependencies": {
134
- "@aifabrix/miso-client": "4.23.1",
136
+ "@aifabrix/miso-client": "5.0.3",
135
137
  "@azure/identity": "4.11.1",
136
138
  "adm-zip": "^0.6.1",
137
139
  "ajv": "^8.20.0",
@@ -1,5 +1,5 @@
1
1
  schemaVersion: "1"
2
- kitVersion: "1.2.0"
2
+ kitVersion: "1.2.1"
3
3
  minimumBuilderVersion: "2.54.2"
4
4
  skills:
5
5
  - aifabrix-plan
@@ -20,7 +20,7 @@ Resolve phase: explicit argument → ask. Package state informs the recommendati
20
20
  the action.
21
21
 
22
22
  Stop at the phase gate. Do not run the next phase without `next-yes`
23
- ([interaction.md](../aifabrix-plan/references/interaction.md)).
23
+ ([interaction.md](../shared/interaction.md)).
24
24
 
25
25
  | Phase | Entry | Success |
26
26
  | --- | --- | --- |
@@ -67,6 +67,7 @@ System key: argument → active plan → sole `integration/` folder → ask once
67
67
 
68
68
  ## References
69
69
 
70
+ - [interaction.md](../shared/interaction.md) — native selection controls, routes, and phase gates
70
71
  - [status.md](../shared/status.md) — read-only default, route options, status matrix
71
72
  - [delivery-gates.md](references/delivery-gates.md) — BID/wizard/repair, upload vs deploy, tests, identity, protection
72
73
  - [contract-checklist.md](references/contract-checklist.md) — exposed, FK, viewpoints, RBAC/ABAC
@@ -39,6 +39,21 @@ If `integration/<systemKey>/deploy.js` exists, you may run it as the publish lad
39
39
 
40
40
  Never mark live E2E pass when it did not run.
41
41
 
42
+ ## Upsert authoring and warnings
43
+
44
+ When upsert is explicitly required, first run
45
+ `aifabrix datasource capability upsert <file-or-key> --dry-run --json`. Apply a
46
+ fix automatically only when the finding identifies a deterministic local
47
+ manifest change and the active phase authorizes mutation. Rerun the exact
48
+ failed command before broader validation.
49
+
50
+ Every open finding must state the observed condition, business consequence,
51
+ safe action, prevention, residual risk, and exact rerun. A source API that
52
+ cannot constrain the complete business identity is not AI-fixable: keep valid
53
+ separate create/update capabilities, report duplicate or wrong-update risk,
54
+ and withhold verified upsert. Never invent a lookup, weaken the two-write
55
+ no-duplicate assertion, or hide the warning behind another passing test.
56
+
42
57
  ## Identity and protection order
43
58
 
44
59
  1. `aifabrix auth status` — if unauthenticated, [login.md](../../shared/login.md)
@@ -16,7 +16,7 @@ Named with no specification, report what exists and ask; do not start writing a
16
16
  an inferred spec ([status.md](../shared/status.md)).
17
17
 
18
18
  Score completeness with [challenge-checklist.md](references/challenge-checklist.md).
19
- Next-step interaction: [interaction.md](references/interaction.md).
19
+ Structured choices and phase gates: [interaction.md](../shared/interaction.md).
20
20
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
21
21
  CLI login from chat: [login.md](../shared/login.md).
22
22
 
@@ -32,7 +32,8 @@ Do not start Connected System validate until **PLAN_READY** and the user picks `
32
32
 
33
33
  1. Collect the spec the user named. Do not mine platform repos as the spec.
34
34
  2. Inventory without adding: purpose, sources, entities, relationships, role labels, operations, Viewpoints, Evidence, identity join. Missing sections → `TBD` plus an Open question.
35
- 3. Challenge required rows. AskQuestion every MISSING required row. Authority and identity cannot be guessed.
35
+ 3. Challenge required rows. Use the structured question control for every MISSING required
36
+ row. Authority and identity cannot be guessed.
36
37
  4. Write or update `.cursor/plans/{major}.0-{slug}.plan.md` using the template H2 order. Same capability updates in place.
37
38
  5. Report status and path. On PLAN_READY, offer validate — do not run it without `next-yes`.
38
39
 
@@ -38,8 +38,10 @@ business-case readiness. None of them, alone or together, makes prove READY.
38
38
 
39
39
  Every phase **must** update [learning-template.md](references/learning-template.md).
40
40
  Evidence rules: [evidence-lifecycle.md](references/evidence-lifecycle.md).
41
+ Role Assistant test execution and diagnosis:
42
+ [testing-playbook.md](../aifabrix-role-assistant/references/testing-playbook.md).
41
43
  Demo shape: [demo-template.md](references/demo-template.md).
42
- Next-step: [interaction.md](../aifabrix-plan/references/interaction.md).
44
+ Structured choices and phase gates: [interaction.md](../shared/interaction.md).
43
45
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
44
46
  CLI login from chat: [login.md](../shared/login.md).
45
47
 
@@ -60,7 +62,9 @@ Repeatable: demo, Knowledge, candidate Evidence, tests. Do not refuse because va
60
62
 
61
63
  ## Prove
62
64
 
63
- Run assistant tests. Record observations and gap owner (`none` | `product-gap` | `missing-customer-fact` | `scope-decision`).
65
+ Run assistant tests using the testing playbook. Record the business verdict,
66
+ execution reference, and gap owner (`none` | `product-gap` |
67
+ `missing-customer-fact` | `scope-decision`).
64
68
  Happy + safe-stop coverage for authority scenarios.
65
69
  Never report candidates as certified.
66
70
 
@@ -49,6 +49,25 @@ Promote candidate → governed Evidence only when:
49
49
  3. Contract validation + certification path for Evidence completed (public Evidence Fabrix / operate-first RA docs)
50
50
  4. learning.md records promotion date + commit
51
51
 
52
+ ## Human authority and applied proof
53
+
54
+ Keep these states distinct:
55
+
56
+ | State | What it proves |
57
+ | --- | --- |
58
+ | Candidate or proposal | Draft content exists; it is not governed Evidence |
59
+ | Certified | The applicable human governance decision completed |
60
+ | Active | The product may supply the Evidence to execution |
61
+ | Applied | A recorded execution shows the Evidence affected the result |
62
+
63
+ An agent may prepare candidates, execute authorized source cases, and inspect a
64
+ proposal. It must pause before certifying, activating, rejecting, or
65
+ deactivating Evidence unless the current user explicitly authorizes that action
66
+ and the product workflow permits it. Record the pause as `BLOCKED_BY_HUMAN`, not
67
+ as a failed test. After the human gate, rerun the case and prove Applied Evidence
68
+ from execution evidence; catalog presence is not Applied proof. Follow the
69
+ [Role Assistant testing playbook](../../aifabrix-role-assistant/references/testing-playbook.md).
70
+
52
71
  ## Tests
53
72
 
54
73
  For each in-scope scenario ID:
@@ -66,3 +85,6 @@ Prefer `role-assistant/ra-<roleKey>/tests/<suiteId>/` in this customer repo (pac
66
85
  - Hiding process law in Knowledge
67
86
  - Uploading candidates as if certified
68
87
  - Skipping safe-stop coverage for authority scenarios
88
+ - Certifying, activating, rejecting, or deactivating Evidence without the
89
+ current user's explicit authority
90
+ - Reporting catalog presence as proof that Evidence was Applied
@@ -9,8 +9,11 @@ description: >
9
9
  # aifabrix-role-assistant
10
10
 
11
11
  See [package-boundary.md](references/package-boundary.md) and nested `role-assistant/AGENTS.md`.
12
+ For test execution, verdicts, fixtures, and diagnosis, follow
13
+ [testing-playbook.md](references/testing-playbook.md).
12
14
  Host runtimes (local vs SSH): [hosts.md](../shared/hosts.md).
13
15
  CLI login from chat: [login.md](../shared/login.md).
16
+ Structured choices and phase gates: [interaction.md](../shared/interaction.md).
14
17
 
15
18
  Named with no task, report status and stop: [status.md](../shared/status.md).
16
19
 
@@ -47,7 +50,9 @@ substitutes for it.
47
50
 
48
51
  1. `aifabrix download <catalogRoleOrRaKey>` before editing an existing assistant.
49
52
  2. Author settings, Knowledge, Evidence, tests, and sidecars per current Builder contracts. Read `aifabrix role-assistant --help`.
50
- 3. `aifabrix role-assistant test …` is allowed without local RA publish.
53
+ 3. `aifabrix role-assistant test …` is allowed without local RA publish. Apply
54
+ the testing playbook and inspect hard business assertions before reporting
55
+ READY or VERIFIED.
51
56
  4. Upload/deploy RA only when the user explicitly asks in this turn.
52
57
  5. Report results honestly.
53
58
 
@@ -62,3 +67,4 @@ substitutes for it.
62
67
  ## Trigger examples
63
68
 
64
69
  - Create or update the Finance Controller Role Assistant
70
+ - Test or diagnose the Finance Controller Role Assistant
@@ -0,0 +1,117 @@
1
+ # Role Assistant testing and diagnosis
2
+
3
+ Public concepts: https://docs.aifabrix.ai/docs/role-assistants
4
+ Exact commands, fields, and flags: run `aifabrix role-assistant test --help` with
5
+ the installed Builder version.
6
+
7
+ Use this playbook for live Role Assistant cases. A successful command is not
8
+ enough: the observed business outcome, authority boundary, capabilities, Work
9
+ Result, and resource changes must match the case.
10
+
11
+ ## Preconditions
12
+
13
+ - Authenticate to the intended environment and identify the package and cases
14
+ root before execution.
15
+ - Confirm every required Connected System is published and certified, its
16
+ required capabilities are available, and worker availability is known.
17
+ - Record the actor and business role, mutation scope, designated test subjects,
18
+ and cleanup rule before a mutating case.
19
+ - Treat missing auth, worker availability, capability, fixture, or required
20
+ human action as `BLOCKED` with the exact prerequisite.
21
+
22
+ ## Execution ladder
23
+
24
+ 1. Validate the package and use `--list-cases` to confirm discovery.
25
+ 2. Run one focused case with `--case ... --json`.
26
+ 3. Inspect lifecycle and final Runtime status, questions or approvals,
27
+ `expected.capabilities`, Work Result completion, resource changes, and
28
+ Evidence use. Do not rely on the process exit or wrapper verdict alone.
29
+ 4. Correct the owning layer, rerun the exact case, and then run its suite.
30
+ 5. Run broader suites only after the focused case is stable.
31
+ 6. Record environment, timestamp, package/source revision, command,
32
+ execution/correlation reference, verdict class, and gap owner in
33
+ `learning/learning.md` or the plan-defined evidence location.
34
+
35
+ ## Verdict law
36
+
37
+ | Observation | Verdict |
38
+ | --- | --- |
39
+ | Hard assertions and intended business outcome pass | `PASS` |
40
+ | Intended denial or safe stop occurs with no forbidden mutation | `PASS_EXPECTED_STOP` |
41
+ | `expected.soft: true` turns a mismatch into a warning | `GAP`; never `VERIFIED` |
42
+ | A prerequisite or required human action is absent | `BLOCKED` with the missing row |
43
+ | Runtime or CLI is defective and the run is reproducible | `PRODUCT_GAP` |
44
+ | Role Assistant, Knowledge, or Evidence content is wrong while the product behaves correctly | `PACKAGE_GAP` |
45
+
46
+ An exit code `0`, CLI `PASS`, lifecycle smoke, or certification level cannot
47
+ replace the applicable business verdict. An expected safe stop is not ordinary
48
+ success: name the denied or waiting state and prove that no forbidden mutation
49
+ occurred.
50
+
51
+ ## Assertion quality
52
+
53
+ - Release and prove cases use hard lifecycle and Result assertions. Soft cases
54
+ are exploratory and cannot establish READY or VERIFIED.
55
+ - Mutation cases assert the exact required and forbidden capabilities,
56
+ `expected.honesty.requireWorkResult`, completion, and a meaningful
57
+ `expected.honesty.minResourceChanges` value.
58
+ - Read-only cases forbid mutation capabilities. Authority cases assert the
59
+ intended denial, question, approval wait, or safe stop.
60
+ - Do not weaken an assertion to accommodate a failure. Classify the gap and
61
+ repair its owner.
62
+
63
+ ## Fixtures, identity, and cleanup
64
+
65
+ - Stable shared identities are read-only unless the integration README names
66
+ them as approved mutable subjects.
67
+ - Give mutable cases collision-safe explicit values. Generate and write those
68
+ values into the case through normal repository editing before the run; do not
69
+ claim that an interpolation token exists unless current CLI help, schema,
70
+ source, and tests establish it.
71
+ - Share an identity across cases only for a documented suite dependency.
72
+ - Never inject a hidden primary key, actor, business role, API key, or
73
+ authorization dimension to make a case pass.
74
+ - Cleanup must be explicit, bounded to the designated subject, and separately
75
+ authorized when destructive.
76
+
77
+ ## ABAC and replay
78
+
79
+ Every authority-sensitive scenario needs an allowed case and an outside-scope
80
+ or denied case. Nested calls must preserve the same actor and business-role
81
+ context. Ambiguous identity must ask or stop safely, never select a convenient
82
+ record. Retry and replay cases must prove there is no duplicate resource and no
83
+ double-counted Evidence.
84
+
85
+ ## Human Evidence boundary
86
+
87
+ Agents may author candidates, run source executions, and inspect proposals when
88
+ authorized. They must not certify, activate, reject, or deactivate Evidence for
89
+ a human unless the current user explicitly authorizes that product action and
90
+ the product workflow permits it. A run paused at certification or activation is
91
+ `BLOCKED_BY_HUMAN`, not failed. Later proof must show Evidence was actually
92
+ Applied in execution; catalog presence alone is insufficient. See
93
+ [Evidence lifecycle](../../aifabrix-prove/references/evidence-lifecycle.md).
94
+
95
+ ## Root-cause routing
96
+
97
+ | Finding | Owner / next action |
98
+ | --- | --- |
99
+ | Auth, environment, or worker unavailable | Report the exact prerequisite |
100
+ | Duplicate or stale test data | Fixture owner; preserve the trail and repair safely |
101
+ | Soft or incomplete expectation | Case author; strengthen without changing product behavior |
102
+ | Missing operation, certification, or capability | Connected System package |
103
+ | Runtime loop, wrong identity binding, or false success | Product gap with execution evidence |
104
+ | Wrong prompt, Knowledge, or Evidence definition | Role Assistant package |
105
+ | Evidence certification or activation required | Human governance gate |
106
+
107
+ Do not conceal a lower-layer gap in Knowledge or build a Role Assistant-local
108
+ imitation of a missing Connected System capability.
109
+
110
+ For an upsert finding, route local manifest repair to Connected System
111
+ ownership and rerun the exact failed proof. If the external source cannot
112
+ support exact business-identity lookup or uniqueness, state what happened,
113
+ what cannot be repaired by AI, the duplicate or wrong-record-update risk, the
114
+ safe alternative, and how future connectors should prevent it. Keep readiness
115
+ blocked or qualified as reported by validation; never substitute create,
116
+ weaken the no-duplicate assertion, or clear the warning because an unrelated
117
+ case passed.