@aifabrix/builder 2.51.0 → 2.51.1

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 (162) hide show
  1. package/jest.isolated-projects.js +77 -1
  2. package/jest.projects.js +5 -426
  3. package/lib/api/assistants.api.js +67 -0
  4. package/lib/api/datasources-extended.api.js +6 -6
  5. package/lib/api/evidence-authoring-helpers.js +267 -0
  6. package/lib/api/evidence-authoring.api.js +337 -0
  7. package/lib/api/evidence.api.js +57 -1
  8. package/lib/api/index.js +24 -7
  9. package/lib/api/pipeline.api.js +43 -3
  10. package/lib/api/records-bulk.api.js +28 -3
  11. package/lib/api/types/evidence.types.js +156 -8
  12. package/lib/api/work.api.js +283 -0
  13. package/lib/app/run-deploy-job.js +108 -0
  14. package/lib/app/run-helpers.js +10 -3
  15. package/lib/app/run.js +8 -0
  16. package/lib/channels/add-channel.js +54 -23
  17. package/lib/channels/approval-guides/chatgpt.js +8 -7
  18. package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
  19. package/lib/channels/channel-artifact-generator.js +14 -2
  20. package/lib/channels/channel-credential.js +3 -1
  21. package/lib/channels/channel-probe-evidence.js +79 -0
  22. package/lib/channels/channel-probe.js +265 -13
  23. package/lib/channels/channel-runtime-resolver.js +120 -29
  24. package/lib/channels/channel-status-display.js +44 -0
  25. package/lib/channels/channel-targets.js +7 -0
  26. package/lib/channels/chatgpt-runtime-contract.js +29 -0
  27. package/lib/channels/microsoft-copilot-package.js +280 -0
  28. package/lib/channels/runtime-tool-catalog.js +165 -0
  29. package/lib/cli/index.js +2 -0
  30. package/lib/cli/setup-app.js +4 -0
  31. package/lib/cli/setup-external-system-help.js +8 -2
  32. package/lib/cli/setup-external-system.js +12 -0
  33. package/lib/commands/app-test.js +2 -14
  34. package/lib/commands/datasource-load-export-cli.js +6 -1
  35. package/lib/commands/deploy-gate-failure-details.js +19 -17
  36. package/lib/commands/dev-down.js +2 -6
  37. package/lib/commands/evidence-kind.js +65 -15
  38. package/lib/commands/governance-generate-external.js +56 -21
  39. package/lib/commands/lifecycle-external.js +10 -4
  40. package/lib/commands/repair-datasource-apply.js +10 -1
  41. package/lib/commands/repair-datasource-run.js +4 -2
  42. package/lib/commands/repair-datasource-test-payload.js +80 -19
  43. package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
  44. package/lib/commands/repair-internal.js +9 -1
  45. package/lib/commands/repair-openapi-sync.js +34 -17
  46. package/lib/commands/role-assistant.js +313 -0
  47. package/lib/commands/upload-data-sync-finalize.js +5 -4
  48. package/lib/commands/upload-sidecar-followups.js +144 -47
  49. package/lib/commands/upload.js +43 -6
  50. package/lib/commands/verify-governance-combined-external.js +73 -9
  51. package/lib/commands/verify-governance-db.js +80 -25
  52. package/lib/commands/verify-governance-external.js +29 -5
  53. package/lib/commands/wizard-core-helpers.js +7 -3
  54. package/lib/commands/wizard-core.js +78 -9
  55. package/lib/datasource/bulk-loader-run-batches.js +4 -2
  56. package/lib/datasource/bulk-loader-service.js +5 -1
  57. package/lib/datasource/datasource-validate-summary.js +2 -2
  58. package/lib/datasource/validate.js +13 -6
  59. package/lib/external-system/deploy-helpers.js +35 -1
  60. package/lib/external-system/deploy.js +23 -10
  61. package/lib/external-system/download-helpers.js +111 -2
  62. package/lib/external-system/download-role-assistant.js +55 -0
  63. package/lib/external-system/download.js +45 -26
  64. package/lib/external-system/test-helpers.js +1 -1
  65. package/lib/external-system/test.js +39 -3
  66. package/lib/generator/builders.js +3 -0
  67. package/lib/generator/wizard.js +51 -4
  68. package/lib/lifecycle/access-control-abac.js +121 -0
  69. package/lib/lifecycle/lifecycle-governance-options.js +4 -1
  70. package/lib/lifecycle/product-model.js +3 -2
  71. package/lib/lifecycle/report-display.js +14 -2
  72. package/lib/programmatic/capability-registry.js +20 -1
  73. package/lib/programmatic/command-options.js +1 -0
  74. package/lib/programmatic/index.js +4 -0
  75. package/lib/programmatic/manifest-patch-section.js +48 -9
  76. package/lib/programmatic/manifest-section-registry-data.js +3 -2
  77. package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
  78. package/lib/programmatic/openapi-component-schemas.js +16 -1
  79. package/lib/programmatic/openapi-description-enricher.js +3 -1
  80. package/lib/programmatic/openapi-descriptions.js +55 -5
  81. package/lib/programmatic/openapi-evidence-schemas.js +161 -0
  82. package/lib/programmatic/openapi-manifest-schemas.js +6 -2
  83. package/lib/programmatic/openapi-operation-bindings.js +99 -0
  84. package/lib/programmatic/openapi-request-schemas.js +36 -1
  85. package/lib/programmatic/openapi-result-schemas.js +1 -1
  86. package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
  87. package/lib/programmatic/openapi-schema-descriptions-core.js +1 -1
  88. package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
  89. package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
  90. package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
  91. package/lib/programmatic/openapi-spec.js +47 -28
  92. package/lib/programmatic/orchestration-openapi-meta.js +5 -0
  93. package/lib/programmatic/request-timeout.js +3 -0
  94. package/lib/programmatic/route-handler-map.js +89 -2
  95. package/lib/programmatic/run-certify.js +15 -2
  96. package/lib/programmatic/run-deploy.js +74 -0
  97. package/lib/programmatic/run-evidence-authoring.js +80 -0
  98. package/lib/programmatic/run-protection.js +124 -0
  99. package/lib/programmatic/run-template-workspace.js +28 -1
  100. package/lib/programmatic/workspace-context.js +6 -0
  101. package/lib/protection/auth-context.js +48 -6
  102. package/lib/protection/paths.js +22 -0
  103. package/lib/protection/run-protection-create-helpers.js +22 -1
  104. package/lib/protection/run-protection-create.js +1 -1
  105. package/lib/protection/sync-after-upload.js +1 -1
  106. package/lib/role-assistant/evidence-sync.js +450 -0
  107. package/lib/role-assistant/github-workflow.js +96 -0
  108. package/lib/role-assistant/knowledge-sync.js +465 -0
  109. package/lib/role-assistant/settings.js +126 -0
  110. package/lib/role-assistant/test-cases-evaluate.js +170 -0
  111. package/lib/role-assistant/test-cases-honesty.js +184 -0
  112. package/lib/role-assistant/test-cases.js +364 -0
  113. package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
  114. package/lib/role-assistant/test-runner-workhub.js +385 -0
  115. package/lib/role-assistant/test-runner.js +500 -0
  116. package/lib/schema/application-schema.json +16 -2
  117. package/lib/schema/deployment-rules.yaml +1 -0
  118. package/lib/schema/external-datasource.schema.json +38 -4
  119. package/lib/schema/external-system.schema.json +4 -4
  120. package/lib/schema/infra.parameter.yaml +20 -0
  121. package/lib/schema/role-assistant-settings.schema.json +108 -0
  122. package/lib/utils/api.js +52 -27
  123. package/lib/utils/app-path-resolve.js +72 -24
  124. package/lib/utils/authentication-shape.js +3 -1
  125. package/lib/utils/deployment-validation.js +28 -3
  126. package/lib/utils/docker-run-ephemeral.js +55 -0
  127. package/lib/utils/enterprise-knowledge-paths.js +29 -12
  128. package/lib/utils/env-copy-keycloak.js +196 -0
  129. package/lib/utils/env-copy.js +34 -1
  130. package/lib/utils/external-system-readiness-compact-display.js +3 -0
  131. package/lib/utils/help-builder.js +1 -0
  132. package/lib/utils/paths.js +67 -23
  133. package/lib/utils/pipeline-upload-auth-retry.js +15 -3
  134. package/lib/utils/role-assistant-paths.js +98 -0
  135. package/lib/utils/schema-loader.js +41 -0
  136. package/lib/utils/schema-resolver.js +36 -4
  137. package/lib/utils/token-manager.js +38 -29
  138. package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
  139. package/lib/validation/external-manifest-validator.js +31 -8
  140. package/lib/validation/validate-external-file.js +8 -1
  141. package/package.json +2 -1
  142. package/scripts/export-builder-api-evidence.js +1 -1
  143. package/templates/applications/dataplane/application.yaml +23 -15
  144. package/templates/applications/dataplane/env.template +1 -1
  145. package/templates/applications/dataplane/rbac.yaml +14 -0
  146. package/templates/applications/miso-controller/application.yaml +1 -1
  147. package/templates/applications/miso-controller/env.template +3 -1
  148. package/templates/channels/chatgpt/install.template.md.hbs +42 -24
  149. package/templates/channels/chatgpt/integration/README.md +10 -7
  150. package/templates/channels/chatgpt/integration/application.json +2 -2
  151. package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
  152. package/templates/channels/chatgpt/integration/env.template +1 -1
  153. package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
  154. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
  155. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
  156. package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
  157. package/templates/channels/microsoft-copilot/integration/README.md +9 -7
  158. package/templates/channels/microsoft-copilot/integration/application.json +2 -2
  159. package/templates/channels/microsoft-copilot/integration/env.template +1 -1
  160. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
  161. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
  162. /package/.cursor/plans/{157.1-security-quality-violations-by-type.csv → Done/157.1-security-quality-violations-by-type.csv} +0 -0
@@ -7,7 +7,7 @@
7
7
 
8
8
  'use strict';
9
9
 
10
- /** @typedef {'VERIFIED'|'FAILED'|'NOT_VERIFIED'} LifecycleVerdict */
10
+ /** @typedef {'VERIFIED'|'FAILED'|'NOT_VERIFIED'|'NOT_APPLICABLE'} LifecycleVerdict */
11
11
 
12
12
  /** @typedef {'BRONZE'|'SILVER'|'GOLD'|'PLATINUM'|'NONE'} CertificationLevel */
13
13
 
@@ -16,7 +16,8 @@
16
16
  const VERDICT = Object.freeze({
17
17
  VERIFIED: 'VERIFIED',
18
18
  FAILED: 'FAILED',
19
- NOT_VERIFIED: 'NOT_VERIFIED'
19
+ NOT_VERIFIED: 'NOT_VERIFIED',
20
+ NOT_APPLICABLE: 'NOT_APPLICABLE'
20
21
  });
21
22
 
22
23
  const CERTIFICATION_LEVEL = Object.freeze({
@@ -58,9 +58,13 @@ function displayHeroMetric(label, percent) {
58
58
  logger.log('');
59
59
  }
60
60
 
61
- function displayVerdictLine(verdict, verifiedLabel, failedLabel) {
61
+ function displayVerdictLine(verdict, verifiedLabel, failedLabel, notApplicableLabel) {
62
62
  if (verdict === VERDICT.VERIFIED) {
63
63
  logger.log(formatSuccessParagraph(verifiedLabel));
64
+ } else if (verdict === VERDICT.NOT_APPLICABLE) {
65
+ logger.log(
66
+ formatSuccessParagraph(notApplicableLabel || 'NOT APPLICABLE')
67
+ );
64
68
  } else {
65
69
  logger.log(formatBlockingError(failedLabel));
66
70
  }
@@ -196,7 +200,12 @@ function displayVerifyGovernanceTTY(systemKey, result, opts = {}) {
196
200
  logger.log(chalk.white('Enforcement Scenarios'));
197
201
  logger.log(chalk.cyan.bold(`${passed}/${total} Passed`));
198
202
  logger.log('');
199
- displayVerdictLine(result.verdict, 'GOVERNANCE VERIFIED', 'GOVERNANCE FAILED');
203
+ displayVerdictLine(
204
+ result.verdict,
205
+ 'GOVERNANCE VERIFIED',
206
+ 'GOVERNANCE FAILED',
207
+ 'GOVERNANCE NOT APPLICABLE'
208
+ );
200
209
  if (opts.details === true) {
201
210
  displayGovernanceVerboseDetails(result);
202
211
  }
@@ -209,6 +218,9 @@ function formatPillarLine(label, verdict) {
209
218
  if (verdict === VERDICT.FAILED) {
210
219
  return `${label.padEnd(24)}${failureGlyph()} FAILED`;
211
220
  }
221
+ if (verdict === VERDICT.NOT_APPLICABLE) {
222
+ return `${label.padEnd(24)}— NOT APPLICABLE`;
223
+ }
212
224
  return `${label.padEnd(24)}— NOT VERIFIED`;
213
225
  }
214
226
 
@@ -40,6 +40,21 @@ const CAPABILITIES = Object.freeze([
40
40
  { method: 'POST', route: '/systems/:systemKey/certify/:pillar', handlerId: 'runCertifyPillar', operationType: 'certify', mutating: true },
41
41
  { method: 'GET', route: '/systems/:systemKey/lifecycle', handlerId: 'getLifecycleReport', operationType: 'certify.read', mutating: false },
42
42
  { method: 'POST', route: '/systems/:systemKey/upload', handlerId: 'runUpload', operationType: 'apply.dev', mutating: true },
43
+ { method: 'POST', route: '/systems/:systemKey/deploy', handlerId: 'runDeploySystem', operationType: 'apply.prod', mutating: true },
44
+ {
45
+ method: 'POST',
46
+ route: '/systems/:systemKey/datasources/:datasourceKey/protection/upload',
47
+ handlerId: 'runProtectionUpload',
48
+ operationType: 'apply.protection',
49
+ mutating: true
50
+ },
51
+ {
52
+ method: 'POST',
53
+ route: '/systems/:systemKey/datasources/:datasourceKey/protection/create',
54
+ handlerId: 'runProtectionCreate',
55
+ operationType: 'apply.protection',
56
+ mutating: true
57
+ },
43
58
  { method: 'POST', route: '/systems/:systemKey/template/materialize', handlerId: 'materializeTemplate', operationType: 'template', mutating: true },
44
59
  { method: 'GET', route: '/systems/:systemKey/workspace/status', handlerId: 'getWorkspaceStatus', operationType: 'workspace.read', mutating: false },
45
60
  { method: 'POST', route: '/systems/:systemKey/workspace/diff', handlerId: 'runWorkspaceDiff', operationType: 'workspace.read', mutating: false },
@@ -49,7 +64,11 @@ const CAPABILITIES = Object.freeze([
49
64
  { method: 'GET', route: '/systems/:systemKey/repository/status', handlerId: 'getRepositoryStatus', operationType: 'repository.read', mutating: false },
50
65
  { method: 'POST', route: '/systems/:systemKey/repository/dispatch-pipeline', handlerId: 'runRepositoryDispatchPipeline', operationType: 'repository', mutating: true },
51
66
  { method: 'GET', route: '/health', handlerId: 'health', operationType: 'infra', mutating: false },
52
- { method: 'GET', route: '/health/ready', handlerId: 'ready', operationType: 'infra', mutating: false }
67
+ { method: 'GET', route: '/health/ready', handlerId: 'ready', operationType: 'infra', mutating: false },
68
+ { method: 'GET', route: '/evidence/:roleKey/metadata', handlerId: 'getEvidenceMetadata', operationType: 'evidence.read', mutating: false },
69
+ { method: 'GET', route: '/evidence/:roleKey/download', handlerId: 'downloadEvidence', operationType: 'evidence.read', mutating: false },
70
+ { method: 'POST', route: '/evidence/:roleKey/upload', handlerId: 'uploadEvidenceCandidates', operationType: 'evidence.write', mutating: true },
71
+ { method: 'POST', route: '/evidence/:roleKey/activate', handlerId: 'activateEvidenceCandidates', operationType: 'evidence.activate', mutating: true }
53
72
  ]);
54
73
 
55
74
  /**
@@ -22,6 +22,7 @@ function mergeProgrammaticOptions(ctx, options = {}) {
22
22
  authConfig: auth.authConfig,
23
23
  dataplaneUrl: auth.dataplaneUrl,
24
24
  environment: auth.environment,
25
+ controllerUrl: ctx.controllerUrl || options.controllerUrl,
25
26
  silentResolve: true,
26
27
  minimal: options.minimal !== false
27
28
  };
@@ -6,6 +6,8 @@
6
6
 
7
7
  const workspaceContext = require('./workspace-context');
8
8
  const runUpload = require('./run-upload');
9
+ const runDeploy = require('./run-deploy');
10
+ const runProtection = require('./run-protection');
9
11
  const runWizard = require('./run-wizard');
10
12
  const runRepair = require('./run-repair');
11
13
  const runValidate = require('./run-validate');
@@ -29,6 +31,8 @@ const enterpriseMcpClient = require('./enterprise-mcp-client');
29
31
  module.exports = {
30
32
  ...workspaceContext,
31
33
  ...runUpload,
34
+ ...runDeploy,
35
+ ...runProtection,
32
36
  ...runWizard,
33
37
  ...runRepair,
34
38
  ...runValidate,
@@ -33,7 +33,7 @@ const { runWithWorkspaceContext } = require('./workspace-context');
33
33
 
34
34
  /**
35
35
  * @param {import('./workspace-context').WorkspaceContext} ctx
36
- * @param {{ sectionKey: string, datasourceKey?: string, intent: string, patch: Object, mode: string, sectionReadToken: string, dryRun?: boolean }} body
36
+ * @param {{ sectionKey: string, datasourceKey?: string, intent: string, patch: Object, mode: string, sectionReadToken?: string, dryRun?: boolean }} body
37
37
  */
38
38
  async function patchManifestSection(ctx, body) {
39
39
  return runWithWorkspaceContext(ctx, async() => {
@@ -43,10 +43,11 @@ async function patchManifestSection(ctx, body) {
43
43
 
44
44
  const loaded = loadTargetDocument(ctx, def.target, body.datasourceKey);
45
45
  const beforeValue = extractSectionValue(loaded.doc, def);
46
- verifyReadToken(body, beforeValue);
46
+ assertSectionReadToken(body, beforeValue);
47
47
 
48
48
  const draft = JSON.parse(JSON.stringify(loaded.doc));
49
- injectSectionValue(draft, def, body.patch, body.mode === 'replace' ? 'replace' : 'merge');
49
+ const patchValue = normalizeSectionPatch(def, body.patch);
50
+ injectSectionValue(draft, def, patchValue, body.mode === 'replace' ? 'replace' : 'merge');
50
51
  validateDraftDocument(def.target, draft);
51
52
 
52
53
  const afterValue = extractSectionValue(draft, def);
@@ -58,6 +59,47 @@ async function patchManifestSection(ctx, body) {
58
59
  });
59
60
  }
60
61
 
62
+ /**
63
+ * Scalar single-path sections (resourceType, entityType, syncBridge, …) accept
64
+ * `{ value: <scalar> }` or `{ <leaf>: <scalar> }` because OpenAPI types `patch` as object.
65
+ *
66
+ * @param {import('./manifest-section-registry-data').SectionDefinition} def
67
+ * @param {Object} patch
68
+ * @returns {*}
69
+ */
70
+ function normalizeSectionPatch(def, patch) {
71
+ if (!def || !Array.isArray(def.paths) || def.paths.length !== 1) {
72
+ return patch;
73
+ }
74
+ if (!patch || typeof patch !== 'object' || Array.isArray(patch)) {
75
+ return patch;
76
+ }
77
+ const keys = Object.keys(patch);
78
+ if (keys.length !== 1) {
79
+ return patch;
80
+ }
81
+ const leaf = def.paths[0].includes('.') ? def.paths[0].split('.').pop() : def.paths[0];
82
+ if (keys[0] === 'value' || keys[0] === leaf) {
83
+ return patch[keys[0]];
84
+ }
85
+ return patch;
86
+ }
87
+
88
+ /**
89
+ * When sectionReadToken is omitted, trust the freshly loaded section value
90
+ * (CLI patch-section parity — plan 496.2). Explicit tokens still verify.
91
+ *
92
+ * @param {Object} body
93
+ * @param {*} beforeValue
94
+ */
95
+ function assertSectionReadToken(body, beforeValue) {
96
+ const token = typeof body.sectionReadToken === 'string' ? body.sectionReadToken.trim() : '';
97
+ if (!token) {
98
+ return;
99
+ }
100
+ verifyReadToken({ ...body, sectionReadToken: token }, beforeValue);
101
+ }
102
+
61
103
  /**
62
104
  * @param {Object} body
63
105
  * @param {*} beforeValue
@@ -162,11 +204,6 @@ function assertPatchRequest(body) {
162
204
  err.statusCode = 400;
163
205
  throw err;
164
206
  }
165
- if (!body.sectionReadToken) {
166
- const err = new Error('sectionReadToken is required — GET the section first');
167
- err.statusCode = 400;
168
- throw err;
169
- }
170
207
  if (body.mode !== 'merge' && body.mode !== 'replace') {
171
208
  const err = new Error('mode must be merge or replace');
172
209
  err.statusCode = 400;
@@ -248,5 +285,7 @@ function buildRiskHints(sectionKey, patch, downstream) {
248
285
  }
249
286
 
250
287
  module.exports = {
251
- patchManifestSection
288
+ patchManifestSection,
289
+ normalizeSectionPatch,
290
+ assertSectionReadToken
252
291
  };
@@ -226,11 +226,12 @@ const SECTION_DEFINITIONS = Object.freeze({
226
226
  },
227
227
  'datasource.dimensions': {
228
228
  target: 'datasource',
229
- paths: ['fieldMappings.dimensions'],
229
+ // Canonical root is top-level `dimensions` (fieldMappings.dimensions removed).
230
+ paths: ['dimensions'],
230
231
  mutable: true,
231
232
  required: false,
232
233
  sourceOfTruth: true,
233
- repairHooks: ['fieldMappings']
234
+ repairHooks: []
234
235
  },
235
236
  'datasource.abac': {
236
237
  target: 'datasource',
@@ -0,0 +1,44 @@
1
+ /**
2
+ * OpenAPI result schemas for deploy and protection apply routes.
3
+ *
4
+ * @fileoverview Split from openapi-result-schemas for file-size limits
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { frozenObject, schemaRef } = require('./openapi-schema-helpers');
12
+
13
+ /** @type {Record<string, Object>} */
14
+ const OPENAPI_APPLY_RESULT_SCHEMAS = {
15
+ DeployResultData: frozenObject(
16
+ {
17
+ deployed: { type: 'boolean' },
18
+ deploymentId: { type: 'string' },
19
+ environment: { type: 'string' },
20
+ systemKey: schemaRef('SystemKey')
21
+ },
22
+ ['deployed']
23
+ ),
24
+ ProtectionUploadData: frozenObject(
25
+ {
26
+ datasourceKey: schemaRef('DatasourceKey'),
27
+ uploaded: { type: 'boolean' },
28
+ dryRun: { type: 'boolean' }
29
+ },
30
+ ['datasourceKey', 'uploaded', 'dryRun']
31
+ ),
32
+ ProtectionCreateData: frozenObject(
33
+ {
34
+ datasourceKey: schemaRef('DatasourceKey'),
35
+ created: { type: 'boolean' },
36
+ dryRun: { type: 'boolean' }
37
+ },
38
+ ['datasourceKey', 'created', 'dryRun']
39
+ )
40
+ };
41
+
42
+ module.exports = {
43
+ OPENAPI_APPLY_RESULT_SCHEMAS
44
+ };
@@ -12,10 +12,15 @@ const { enrichOpenApiComponentSchemas } = require('./openapi-description-enriche
12
12
  const { buildSuccessEnvelopeSchema } = require('./openapi-schema-helpers');
13
13
  const { OPENAPI_REQUEST_SCHEMAS } = require('./openapi-request-schemas');
14
14
  const { OPENAPI_RESULT_SCHEMAS } = require('./openapi-result-schemas');
15
+ const { OPENAPI_APPLY_RESULT_SCHEMAS } = require('./openapi-apply-result-schemas');
15
16
  const {
16
17
  OPENAPI_MANIFEST_REQUEST_SCHEMAS,
17
18
  OPENAPI_MANIFEST_RESULT_SCHEMAS
18
19
  } = require('./openapi-manifest-schemas');
20
+ const {
21
+ OPENAPI_EVIDENCE_REQUEST_SCHEMAS,
22
+ OPENAPI_EVIDENCE_RESULT_SCHEMAS
23
+ } = require('./openapi-evidence-schemas');
19
24
 
20
25
  const RESPONSE_ENVELOPES = [
21
26
  ['ListSystemsResponse', 'ListSystemsData'],
@@ -27,6 +32,9 @@ const RESPONSE_ENVELOPES = [
27
32
  ['ValidateResultResponse', 'ValidateResultData'],
28
33
  ['PreviewUploadResponse', 'PreviewUploadData'],
29
34
  ['UploadResultResponse', 'UploadResultData'],
35
+ ['DeployResultResponse', 'DeployResultData'],
36
+ ['ProtectionUploadResponse', 'ProtectionUploadData'],
37
+ ['ProtectionCreateResponse', 'ProtectionCreateData'],
30
38
  ['TestResultResponse', 'TestResultData'],
31
39
  ['CertifyResultResponse', 'CertifyResultData'],
32
40
  ['LifecycleReportResponse', 'LifecycleReportData'],
@@ -39,7 +47,11 @@ const RESPONSE_ENVELOPES = [
39
47
  ['ManifestSectionViewResponse', 'ManifestSectionViewData'],
40
48
  ['ManifestPatchResultResponse', 'ManifestPatchResultData'],
41
49
  ['ManifestSectionsListResponse', 'ManifestSectionsListData'],
42
- ['ManifestRepairResultResponse', 'ManifestRepairResultData']
50
+ ['ManifestRepairResultResponse', 'ManifestRepairResultData'],
51
+ ['EvidenceAuthoringMetadataResponse', 'EvidenceAuthoringMetadataData'],
52
+ ['EvidenceDownloadResponse', 'EvidenceDownloadData'],
53
+ ['EvidenceUploadResponse', 'EvidenceUploadData'],
54
+ ['EvidenceActivateResponse', 'EvidenceActivateData']
43
55
  ];
44
56
 
45
57
  /**
@@ -53,8 +65,11 @@ function buildOpenApiComponentSchemas() {
53
65
  return enrichOpenApiComponentSchemas({
54
66
  ...OPENAPI_REQUEST_SCHEMAS,
55
67
  ...OPENAPI_MANIFEST_REQUEST_SCHEMAS,
68
+ ...OPENAPI_EVIDENCE_REQUEST_SCHEMAS,
56
69
  ...OPENAPI_RESULT_SCHEMAS,
70
+ ...OPENAPI_APPLY_RESULT_SCHEMAS,
57
71
  ...OPENAPI_MANIFEST_RESULT_SCHEMAS,
72
+ ...OPENAPI_EVIDENCE_RESULT_SCHEMAS,
58
73
  EmptyResultData: { type: 'object', properties: {}, additionalProperties: false },
59
74
  ...envelopes
60
75
  });
@@ -14,6 +14,7 @@ const { CERTIFY_SCHEMA_DESCRIPTIONS } = require('./openapi-schema-descriptions-c
14
14
  const { MANIFEST_SCHEMA_DESCRIPTIONS } = require('./openapi-schema-descriptions-manifest');
15
15
  const { MCP_SCHEMA_DESCRIPTIONS } = require('./openapi-schema-descriptions-mcp');
16
16
  const { HELP_SCHEMA_DESCRIPTIONS } = require('./openapi-schema-descriptions-help');
17
+ const { EVIDENCE_SCHEMA_DESCRIPTIONS } = require('./openapi-schema-descriptions-evidence');
17
18
 
18
19
  /** @type {Record<string, Object>} */
19
20
  const MERGED_SCHEMA_DESCRIPTIONS = {
@@ -22,7 +23,8 @@ const MERGED_SCHEMA_DESCRIPTIONS = {
22
23
  ...CERTIFY_SCHEMA_DESCRIPTIONS,
23
24
  ...MANIFEST_SCHEMA_DESCRIPTIONS,
24
25
  ...MCP_SCHEMA_DESCRIPTIONS,
25
- ...HELP_SCHEMA_DESCRIPTIONS
26
+ ...HELP_SCHEMA_DESCRIPTIONS,
27
+ ...EVIDENCE_SCHEMA_DESCRIPTIONS
26
28
  };
27
29
 
28
30
  /**
@@ -53,25 +53,32 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
53
53
  getManifestSection: {
54
54
  summary: 'Read one system manifest section',
55
55
  description:
56
- 'Return section value, schema fragment, quality, dependency graph, and sectionReadToken. MUST call before patchSection.',
56
+ 'Return section value, schema fragment, quality, dependency graph, and sectionReadToken. ' +
57
+ 'Recommended before patch when quality or missingInputs are needed; token is optional on PATCH.',
57
58
  tags: ['manifest']
58
59
  },
59
60
  patchManifestSection: {
60
61
  summary: 'Patch one system manifest section',
61
62
  description:
62
- 'Apply governed merge/replace to a system section. Requires intent and sectionReadToken from GET. Mutating.',
63
+ 'Apply governed merge/replace to a system section. Requires intent. ' +
64
+ 'sectionReadToken from GET is optional — when omitted, the server re-reads the section before write. ' +
65
+ 'Stale tokens return 409. Mutating.',
63
66
  tags: ['manifest']
64
67
  },
65
68
  getDatasourceManifestSection: {
66
69
  summary: 'Read one datasource manifest section',
67
70
  description:
68
- 'Return datasource section value, quality, missingInputs, and sectionReadToken. MUST call before patchSection.',
71
+ 'Return datasource section value, quality, missingInputs, and sectionReadToken. ' +
72
+ 'Recommended before patch when quality or missingInputs are needed; token is optional on PATCH.',
69
73
  tags: ['manifest']
70
74
  },
71
75
  patchDatasourceManifestSection: {
72
76
  summary: 'Patch one datasource manifest section',
73
77
  description:
74
- 'Apply governed merge/replace to a datasource section. Requires intent and sectionReadToken. Mutating.',
78
+ 'Apply governed merge/replace to a datasource section. Requires intent. ' +
79
+ 'sectionReadToken is optional (server re-reads when omitted; stale token → 409). ' +
80
+ 'For scalar sections (e.g. datasource.resourceType), send patch as { "value": "<scalar>" } or ' +
81
+ '{ "<leafName>": "<scalar>" }. Mutating.',
75
82
  tags: ['manifest']
76
83
  },
77
84
  runManifestRepair: {
@@ -115,6 +122,24 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
115
122
  'Full dev publish: validate, sync deploy JSON, push credentials, pipeline upload. Requires external-system:publish in dev. Mutating; use Idempotency-Key; may take up to MAX_UPLOAD_TIMEOUT_MS.',
116
123
  tags: ['apply']
117
124
  },
125
+ runDeploySystem: {
126
+ summary: 'Deploy integration via controller (promote)',
127
+ description:
128
+ 'Controller deploy for external systems (CLI aifabrix deploy parity). Optional body.gate matches CLI --gate (operations,trust,governance,full). Certificate seal is a platform side-effect of published Deploy — not a separate certificates issue call. Mutating; use Idempotency-Key.',
129
+ tags: ['apply']
130
+ },
131
+ runProtectionUpload: {
132
+ summary: 'Upload protection manifest for a datasource',
133
+ description:
134
+ 'Validate and upload the workspace protection manifest for the datasource (CLI aifabrix protection upload parity). Optional sync after upload. Mutating; use Idempotency-Key.',
135
+ tags: ['apply']
136
+ },
137
+ runProtectionCreate: {
138
+ summary: 'Scaffold a local protection manifest',
139
+ description:
140
+ 'Probe dataplane + dimension catalog and write integration/.protection scaffold (CLI aifabrix protection create parity). Mutating; use Idempotency-Key.',
141
+ tags: ['apply']
142
+ },
118
143
  materializeTemplate: {
119
144
  summary: 'Materialize workspace from template',
120
145
  description:
@@ -128,7 +153,8 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
128
153
  },
129
154
  runWorkspaceDiff: {
130
155
  summary: 'Diff workspace draft',
131
- description: 'Invoke Enterprise MCP workspace.diff for local draft vs remote/template baseline.',
156
+ description:
157
+ 'Diff local workspace draft vs remote/template baseline via Enterprise MCP workspace.diff when published; otherwise returns a local draft snapshot (source=local) without failing.',
132
158
  tags: ['workspace']
133
159
  },
134
160
  resetWorkspaceFromTemplate: {
@@ -167,6 +193,30 @@ const OPERATION_DESCRIPTIONS = Object.freeze({
167
193
  summary: 'Readiness probe',
168
194
  description: 'Returns ready with configured dataplaneUrl. Does not verify Dataplane connectivity in V1.',
169
195
  tags: ['infra']
196
+ },
197
+ getEvidenceMetadata: {
198
+ summary: 'Evidence authoring metadata',
199
+ description:
200
+ 'Return live Evidence kinds, aspects, resource types, Role Assistant capabilities, writable fields, and certification/activation constraints for the selected roleKey. Read-only aggregation over Dataplane and Controller catalogs.',
201
+ tags: ['evidence']
202
+ },
203
+ downloadEvidence: {
204
+ summary: 'Download Role Assistant Evidence',
205
+ description:
206
+ 'List or search Evidence for the selected roleKey using existing Dataplane Evidence filters. Read-only projection for AI authoring.',
207
+ tags: ['evidence']
208
+ },
209
+ uploadEvidenceCandidates: {
210
+ summary: 'Upload Evidence candidates',
211
+ description:
212
+ 'Load one or many Evidence rows as inactive candidates, validate each row, and technically certify valid rows. Never activates. Mutating; use Idempotency-Key.',
213
+ tags: ['evidence']
214
+ },
215
+ activateEvidenceCandidates: {
216
+ summary: 'Activate certified Evidence',
217
+ description:
218
+ 'Activate one or many human-approved certified Evidence ids. Organizational gate after technical certification. Mutating; use Idempotency-Key.',
219
+ tags: ['evidence']
170
220
  }
171
221
  });
172
222
 
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Frozen OpenAPI request/result schemas for Evidence authoring routes (162.0).
3
+ *
4
+ * @fileoverview BTA-facing Evidence metadata/download/upload/activate models
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { frozenObject, schemaRef, stringArraySchema } = require('./openapi-schema-helpers');
12
+
13
+ /** @type {Record<string, Object>} */
14
+ const OPENAPI_EVIDENCE_REQUEST_SCHEMAS = {
15
+ EvidenceUploadRow: frozenObject(
16
+ {
17
+ key: { type: 'string' },
18
+ resourceType: { type: 'string' },
19
+ roleKey: { type: 'string' },
20
+ kindKey: { type: 'string' },
21
+ kindVersion: { type: 'string' },
22
+ title: { type: 'string' },
23
+ description: { type: 'string' },
24
+ when: { type: 'string' },
25
+ expectedBehavior: { type: 'string' },
26
+ aspect: { type: 'string' },
27
+ order: { type: 'integer' },
28
+ status: { type: 'string', enum: ['candidate', 'active', 'disabled', 'archived'] }
29
+ },
30
+ ['title', 'when', 'expectedBehavior']
31
+ ),
32
+ EvidenceUploadRequest: frozenObject(
33
+ {
34
+ roleKey: {
35
+ type: 'string',
36
+ description: 'Optional override; defaults to the path roleKey.'
37
+ },
38
+ rows: { type: 'array', items: schemaRef('EvidenceUploadRow') },
39
+ comment: { type: 'string' }
40
+ },
41
+ ['rows']
42
+ ),
43
+ EvidenceActivateRequest: frozenObject(
44
+ {
45
+ evidenceIds: stringArraySchema()
46
+ },
47
+ ['evidenceIds']
48
+ )
49
+ };
50
+
51
+ /** @type {Record<string, Object>} */
52
+ const OPENAPI_EVIDENCE_RESULT_SCHEMAS = {
53
+ EvidenceKindSummary: frozenObject({
54
+ key: { type: ['string', 'null'] },
55
+ version: { type: ['string', 'null'] },
56
+ displayName: { type: ['string', 'null'] },
57
+ purpose: { type: ['string', 'null'] }
58
+ }),
59
+ EvidenceResourceTypeSummary: frozenObject({
60
+ key: { type: ['string', 'null'] },
61
+ displayName: { type: ['string', 'null'] },
62
+ category: { type: ['string', 'null'] }
63
+ }),
64
+ EvidenceAuthoringConstraints: frozenObject({
65
+ certification: frozenObject({
66
+ validationStatus: { type: 'string' },
67
+ candidateStatus: { type: 'string' }
68
+ }),
69
+ activation: frozenObject({
70
+ requiresCertified: { type: 'boolean' },
71
+ requiresAuthority: { type: 'boolean' }
72
+ })
73
+ }),
74
+ EvidenceAuthoringMetadataData: frozenObject(
75
+ {
76
+ roleKey: { type: 'string' },
77
+ evidenceKinds: { type: 'array', items: schemaRef('EvidenceKindSummary') },
78
+ aspects: stringArraySchema(),
79
+ resourceTypes: { type: 'array', items: schemaRef('EvidenceResourceTypeSummary') },
80
+ assistantCapabilities: stringArraySchema(),
81
+ writableFields: stringArraySchema(),
82
+ requiredFields: stringArraySchema(),
83
+ constraints: schemaRef('EvidenceAuthoringConstraints')
84
+ },
85
+ [
86
+ 'roleKey',
87
+ 'evidenceKinds',
88
+ 'aspects',
89
+ 'resourceTypes',
90
+ 'assistantCapabilities',
91
+ 'writableFields',
92
+ 'requiredFields',
93
+ 'constraints'
94
+ ]
95
+ ),
96
+ EvidenceRowProjection: frozenObject({
97
+ id: { type: 'string' },
98
+ key: { type: ['string', 'null'] },
99
+ resourceType: { type: ['string', 'null'] },
100
+ roleKey: { type: ['string', 'null'] },
101
+ kindKey: { type: ['string', 'null'] },
102
+ kindVersion: { type: ['string', 'null'] },
103
+ title: { type: 'string' },
104
+ description: { type: ['string', 'null'] },
105
+ when: { type: 'string' },
106
+ expectedBehavior: { type: 'string' },
107
+ aspect: { type: ['string', 'null'] },
108
+ order: { type: 'integer' },
109
+ status: { type: 'string' },
110
+ fingerprint: { type: ['string', 'null'] }
111
+ }),
112
+ EvidenceDownloadData: frozenObject(
113
+ {
114
+ roleKey: { type: 'string' },
115
+ count: { type: 'integer', minimum: 0 },
116
+ data: { type: 'array', items: schemaRef('EvidenceRowProjection') }
117
+ },
118
+ ['roleKey', 'count', 'data']
119
+ ),
120
+ EvidenceUploadItemOutcome: frozenObject({
121
+ rowIndex: { type: 'integer' },
122
+ evidenceId: { type: ['string', 'null'] },
123
+ evidenceRef: { type: ['string', 'null'] },
124
+ status: { type: 'string' },
125
+ message: { type: 'string' },
126
+ issues: { type: 'array', items: { type: 'string' } }
127
+ }),
128
+ EvidenceUploadData: frozenObject(
129
+ {
130
+ roleKey: { type: 'string' },
131
+ inserted: { type: 'integer', minimum: 0 },
132
+ updated: { type: 'integer', minimum: 0 },
133
+ accepted: { type: 'array', items: schemaRef('EvidenceUploadItemOutcome') },
134
+ rejected: { type: 'array', items: schemaRef('EvidenceUploadItemOutcome') },
135
+ warnings: { type: 'array', items: schemaRef('EvidenceUploadItemOutcome') },
136
+ certified: { type: 'array', items: schemaRef('EvidenceUploadItemOutcome') },
137
+ readyToActivate: { type: 'boolean' }
138
+ },
139
+ ['roleKey', 'accepted', 'rejected', 'warnings', 'certified', 'readyToActivate']
140
+ ),
141
+ EvidenceActivateItemOutcome: frozenObject({
142
+ evidenceId: { type: 'string' },
143
+ result: schemaRef('EvidenceRowProjection'),
144
+ error: { type: 'string' },
145
+ status: { type: ['integer', 'null'] }
146
+ }),
147
+ EvidenceActivateData: frozenObject(
148
+ {
149
+ requested: { type: 'integer', minimum: 0 },
150
+ activated: { type: 'array', items: schemaRef('EvidenceActivateItemOutcome') },
151
+ failed: { type: 'array', items: schemaRef('EvidenceActivateItemOutcome') },
152
+ success: { type: 'boolean' }
153
+ },
154
+ ['requested', 'activated', 'failed', 'success']
155
+ )
156
+ };
157
+
158
+ module.exports = {
159
+ OPENAPI_EVIDENCE_REQUEST_SCHEMAS,
160
+ OPENAPI_EVIDENCE_RESULT_SCHEMAS
161
+ };
@@ -54,10 +54,14 @@ const OPENAPI_MANIFEST_REQUEST_SCHEMAS = {
54
54
  intent: { type: 'string', minLength: 1, description: 'Why this patch is made — audit and repair hints.' },
55
55
  patch: { type: 'object', description: 'Section-scoped merge/replace payload.' },
56
56
  mode: { type: 'string', enum: ['merge', 'replace'], description: 'Merge into or replace section value.' },
57
- sectionReadToken: { type: 'string', description: 'Token from prior GET; stale token returns 409.' },
57
+ sectionReadToken: {
58
+ type: 'string',
59
+ description:
60
+ 'Optional token from prior GET. When omitted, the server re-reads the section immediately before write. Stale token returns 409.'
61
+ },
58
62
  dryRun: { type: 'boolean', description: 'Validate patch without writing files.' }
59
63
  },
60
- ['intent', 'patch', 'mode', 'sectionReadToken']
64
+ ['intent', 'patch', 'mode']
61
65
  ),
62
66
  ManifestRepairRequest: frozenObject(
63
67
  {