@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.
- package/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +172 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +272 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/start.js +310 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/deploy.js +7 -4
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/offer-create-from-template.js +184 -0
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-app.js +8 -1
- package/lib/cli/setup-auth.js +11 -2
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +149 -0
- package/lib/commands/agent-kit.js +417 -0
- package/lib/commands/auth-status-user.js +99 -0
- package/lib/commands/auth-status.js +14 -4
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/datasource-load-export-cli.js +7 -2
- package/lib/commands/datasource-test-trust-cli.js +5 -1
- package/lib/commands/governance-command-action.js +5 -4
- package/lib/commands/identity-membership-role-cache.js +6 -1
- package/lib/commands/lifecycle-command-action.js +16 -15
- package/lib/commands/login-credentials.js +8 -3
- package/lib/commands/login-device.js +14 -5
- package/lib/commands/login.js +14 -1
- package/lib/commands/logout.js +50 -15
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +140 -59
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-governance-command-action.js +9 -7
- package/lib/commands/verify-operations-command-action.js +9 -8
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/commands/verify-trust-command-action.js +6 -5
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +8 -3
- package/lib/generator/helpers.js +12 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +232 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
- package/lib/role-assistant/knowledge-sync.js +9 -126
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +149 -31
- package/lib/role-assistant/test-cases-evidence.js +44 -5
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +297 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +53 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/infra-parameter.schema.json +51 -3
- package/lib/schema/infra.parameter.yaml +476 -40
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/schema/type/document-storage.json +1 -1
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/api-required-permissions.js +271 -0
- package/lib/utils/api.js +16 -13
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/cli-json-exit.js +41 -0
- package/lib/utils/cli-utils.js +23 -5
- package/lib/utils/controller-url.js +78 -4
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/deployment-errors.js +15 -0
- package/lib/utils/deployment-validation-helpers.js +4 -1
- package/lib/utils/device-code.js +31 -10
- package/lib/utils/error-formatters/error-parser.js +16 -1
- package/lib/utils/error-formatters/permission-errors.js +19 -5
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/keycloak-realm-ready.js +5 -2
- package/lib/utils/logger.js +1 -0
- package/lib/utils/paths.js +26 -5
- package/lib/utils/schema-loader.js +37 -0
- package/lib/utils/secrets-helpers.js +2 -2
- package/lib/utils/write-stream-and-wait.js +63 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +20 -6
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +65 -0
- package/templates/agent-kit/skills/shared/login.md +16 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -3
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +21 -2
- package/templates/applications/miso-controller/env.template +96 -25
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- package/templates/typescript/docker-compose.hbs +14 -0
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const { skipRow } = require('./test-case-assertion-row');
|
|
9
|
+
|
|
8
10
|
function asArray(value) {
|
|
9
11
|
return Array.isArray(value) ? value : [];
|
|
10
12
|
}
|
|
@@ -71,10 +73,71 @@ function collectBidSchemaAssertions(expectedBid, observed, assertions, soft) {
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
function
|
|
75
|
-
|
|
76
|
+
function isDatasourceRecord(value) {
|
|
77
|
+
const key = String(value.key || value.datasourceKey || '').trim();
|
|
78
|
+
return Boolean(key && (Array.isArray(value.capabilities) || value.entityType || value.resourceType));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function nestedBidContainers(value) {
|
|
82
|
+
const nested = [];
|
|
83
|
+
if (Array.isArray(value.datasources)) {
|
|
84
|
+
nested.push(value.datasources);
|
|
85
|
+
}
|
|
86
|
+
if (value.spec && typeof value.spec === 'object') {
|
|
87
|
+
nested.push(value.spec);
|
|
88
|
+
}
|
|
89
|
+
if (value.emitted && typeof value.emitted === 'object') {
|
|
90
|
+
nested.push(value.emitted);
|
|
91
|
+
}
|
|
92
|
+
if (value.bid && typeof value.bid === 'object') {
|
|
93
|
+
nested.push(value.bid);
|
|
94
|
+
}
|
|
95
|
+
return nested;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function collectBidObjects(value, acc) {
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
value.forEach((item) => collectBidObjects(item, acc));
|
|
76
101
|
return;
|
|
77
102
|
}
|
|
103
|
+
if (!value || typeof value !== 'object') {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
nestedBidContainers(value).forEach((child) => collectBidObjects(child, acc));
|
|
107
|
+
if (isDatasourceRecord(value)) {
|
|
108
|
+
acc.push(value);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function bidBlob(observed) {
|
|
113
|
+
return observed.emitted || observed.emitRow || observed.applyRow || {};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function datasourceKeysFromBid(observed) {
|
|
117
|
+
const objects = [];
|
|
118
|
+
collectBidObjects(bidBlob(observed), objects);
|
|
119
|
+
return new Set(objects.map((row) => String(row.key || row.datasourceKey || '').trim()).filter(Boolean));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function datasourceHasCapability(observed, datasource, capKey) {
|
|
123
|
+
const objects = [];
|
|
124
|
+
collectBidObjects(bidBlob(observed), objects);
|
|
125
|
+
const match = objects.find((row) => String(row.key || row.datasourceKey || '') === datasource);
|
|
126
|
+
const caps = asArray(match && match.capabilities);
|
|
127
|
+
return caps.some((cap) => String((cap && cap.key) || cap || '') === capKey);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function pushBooleanBidFlag(expectedBid, field, observed, assertions, soft, assertFn) {
|
|
131
|
+
if (expectedBid[field] === false) {
|
|
132
|
+
assertions.push(skipRow(`expected.bid.${field}`, `${field} is false — check skipped.`));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (expectedBid[field] === true) {
|
|
136
|
+
assertFn(expectedBid, observed, assertions, soft);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function assertDefinitionHash(expectedBid, observed, assertions, soft) {
|
|
78
141
|
const { emitRow, applyRow, emitted } = observed;
|
|
79
142
|
const hash =
|
|
80
143
|
(emitted && emitted.definitionHash) ||
|
|
@@ -112,11 +175,60 @@ function assertSuiteRunId(expectedBid, observed, assertions, soft) {
|
|
|
112
175
|
}
|
|
113
176
|
|
|
114
177
|
function collectBidProvenanceAssertions(expectedBid, observed, assertions, soft) {
|
|
115
|
-
|
|
116
|
-
|
|
178
|
+
pushBooleanBidFlag(expectedBid, 'requireDefinitionHash', observed, assertions, soft, assertDefinitionHash);
|
|
179
|
+
pushBooleanBidFlag(expectedBid, 'requireSuiteRunId', observed, assertions, soft, assertSuiteRunId);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function collectBidDatasourceAssertions(expectedBid, observed, assertions, soft) {
|
|
183
|
+
if (Array.isArray(expectedBid.minDatasources)) {
|
|
184
|
+
collectMinDatasources(expectedBid.minDatasources, observed, assertions, soft);
|
|
185
|
+
}
|
|
186
|
+
if (expectedBid.requireCapability && typeof expectedBid.requireCapability === 'object') {
|
|
187
|
+
collectRequireCapability(expectedBid.requireCapability, observed, assertions, soft);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function collectMinDatasources(requiredKeys, observed, assertions, soft) {
|
|
192
|
+
if (requiredKeys.length === 0) {
|
|
193
|
+
assertions.push(skipRow('expected.bid.minDatasources', 'minDatasources is empty — nothing to assert.'));
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const actual = datasourceKeysFromBid(observed);
|
|
197
|
+
for (const raw of requiredKeys) {
|
|
198
|
+
const key = String(raw || '').trim();
|
|
199
|
+
const passed = hasEmitOrApply(observed) && actual.has(key);
|
|
200
|
+
pushBidAssertion(
|
|
201
|
+
assertions,
|
|
202
|
+
soft,
|
|
203
|
+
`expected.bid.minDatasources:${key}`,
|
|
204
|
+
passed,
|
|
205
|
+
`BID lists datasource "${key}".`,
|
|
206
|
+
`BID does not list required datasource "${key}".`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function collectRequireCapability(spec, observed, assertions, soft) {
|
|
212
|
+
const datasource = String(spec.datasource || '').trim();
|
|
213
|
+
const capKey = String(spec.key || '').trim();
|
|
214
|
+
const passed = hasEmitOrApply(observed) && datasourceHasCapability(observed, datasource, capKey);
|
|
215
|
+
pushBidAssertion(
|
|
216
|
+
assertions,
|
|
217
|
+
soft,
|
|
218
|
+
'expected.bid.requireCapability',
|
|
219
|
+
passed,
|
|
220
|
+
`BID datasource "${datasource}" lists capability "${capKey}".`,
|
|
221
|
+
`BID datasource "${datasource}" does not list capability "${capKey}".`
|
|
222
|
+
);
|
|
117
223
|
}
|
|
118
224
|
|
|
119
225
|
function collectBidPathAssertions(expectedBid, observed, assertions, soft) {
|
|
226
|
+
if (expectedBid.forbidGoldPaths === false) {
|
|
227
|
+
assertions.push(
|
|
228
|
+
skipRow('expected.bid.forbidGoldPaths', 'forbidGoldPaths is false — gold-path check skipped.')
|
|
229
|
+
);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
120
232
|
if (expectedBid.forbidGoldPaths === true) {
|
|
121
233
|
const { emitRow, applyRow, emitted } = observed;
|
|
122
234
|
const blob = JSON.stringify(emitted || emitRow || applyRow || {});
|
|
@@ -146,6 +258,7 @@ function collectBidAssertions(expectedBid, observation, options = {}) {
|
|
|
146
258
|
const soft = options.soft === true;
|
|
147
259
|
collectBidSchemaAssertions(expectedBid, observed, assertions, soft);
|
|
148
260
|
collectBidProvenanceAssertions(expectedBid, observed, assertions, soft);
|
|
261
|
+
collectBidDatasourceAssertions(expectedBid, observed, assertions, soft);
|
|
149
262
|
collectBidPathAssertions(expectedBid, observed, assertions, soft);
|
|
150
263
|
return assertions;
|
|
151
264
|
}
|
|
@@ -18,12 +18,41 @@ const {
|
|
|
18
18
|
evaluateSearchExpectations,
|
|
19
19
|
evaluateSkillsExpectations
|
|
20
20
|
} = require('./test-cases-search');
|
|
21
|
+
const { notesRow, withKind } = require('./test-case-assertion-row');
|
|
22
|
+
|
|
23
|
+
const UNSAFE_FACT_SEGMENTS = new Set(['__proto__', 'prototype', 'constructor']);
|
|
24
|
+
const WILDCARD_FACT_SEGMENTS = new Set(['*', '**']);
|
|
25
|
+
|
|
26
|
+
function splitDottedPath(dottedPath) {
|
|
27
|
+
return String(dottedPath || '').split('.').filter((part) => part.length > 0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function factPathSafetyMessage(parts) {
|
|
31
|
+
for (const part of parts) {
|
|
32
|
+
if (UNSAFE_FACT_SEGMENTS.has(part)) {
|
|
33
|
+
return `Fact path rejects prototype segment "${part}" before lookup.`;
|
|
34
|
+
}
|
|
35
|
+
if (WILDCARD_FACT_SEGMENTS.has(part)) {
|
|
36
|
+
return `Fact path rejects wildcard segment "${part}".`;
|
|
37
|
+
}
|
|
38
|
+
if (part === 'latest') {
|
|
39
|
+
return 'Fact path rejects latest-global record lookup.';
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
21
44
|
|
|
22
45
|
function getValueAtPath(obj, dottedPath) {
|
|
23
|
-
const parts =
|
|
46
|
+
const parts = splitDottedPath(dottedPath);
|
|
47
|
+
if (factPathSafetyMessage(parts)) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
24
50
|
let cursor = obj;
|
|
25
51
|
for (const key of parts) {
|
|
26
|
-
if (
|
|
52
|
+
if (cursor === null || cursor === undefined || typeof cursor !== 'object') {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
if (!Object.prototype.hasOwnProperty.call(cursor, key)) {
|
|
27
56
|
return undefined;
|
|
28
57
|
}
|
|
29
58
|
cursor = cursor[key];
|
|
@@ -31,6 +60,10 @@ function getValueAtPath(obj, dottedPath) {
|
|
|
31
60
|
return cursor;
|
|
32
61
|
}
|
|
33
62
|
|
|
63
|
+
function stringifyObserved(value) {
|
|
64
|
+
return value === undefined ? 'missing' : JSON.stringify(value);
|
|
65
|
+
}
|
|
66
|
+
|
|
34
67
|
function evaluateLifecycleStateExpectation(expectedState, observedState) {
|
|
35
68
|
if (!expectedState) {
|
|
36
69
|
return null;
|
|
@@ -46,7 +79,7 @@ function evaluateLifecycleStateExpectation(expectedState, observedState) {
|
|
|
46
79
|
};
|
|
47
80
|
}
|
|
48
81
|
|
|
49
|
-
function evaluateCapabilityExpectations(requiredList, observedCapabilities) {
|
|
82
|
+
function evaluateCapabilityExpectations(kind, requiredList, observedCapabilities) {
|
|
50
83
|
if (!Array.isArray(requiredList) || requiredList.length === 0) {
|
|
51
84
|
return [];
|
|
52
85
|
}
|
|
@@ -55,29 +88,39 @@ function evaluateCapabilityExpectations(requiredList, observedCapabilities) {
|
|
|
55
88
|
const key = String(capability || '').trim();
|
|
56
89
|
const passed = observedSet.has(key);
|
|
57
90
|
return {
|
|
58
|
-
id: `expected.capabilities
|
|
91
|
+
id: `expected.capabilities.${kind}:${key}`,
|
|
59
92
|
passed,
|
|
60
93
|
message: passed
|
|
61
|
-
?
|
|
62
|
-
:
|
|
94
|
+
? `${kind === 'allowed' ? 'Allowed' : 'Required'} capability "${key}" observed.`
|
|
95
|
+
: `${kind === 'allowed' ? 'Allowed' : 'Required'} capability "${key}" was not observed.`
|
|
63
96
|
};
|
|
64
97
|
});
|
|
65
98
|
}
|
|
66
99
|
|
|
100
|
+
function evaluateOneFact(factPath, expectedValue, observation) {
|
|
101
|
+
const parts = splitDottedPath(factPath);
|
|
102
|
+
const safety = factPathSafetyMessage(parts);
|
|
103
|
+
const id = `expected.facts:${factPath}`;
|
|
104
|
+
if (safety) {
|
|
105
|
+
return { id, passed: false, message: safety };
|
|
106
|
+
}
|
|
107
|
+
const actualValue = getValueAtPath(observation, factPath);
|
|
108
|
+
const passed = JSON.stringify(actualValue) === JSON.stringify(expectedValue);
|
|
109
|
+
return {
|
|
110
|
+
id,
|
|
111
|
+
passed,
|
|
112
|
+
message: passed
|
|
113
|
+
? `Fact "${factPath}" matched expected value.`
|
|
114
|
+
: `Fact "${factPath}" expected ${JSON.stringify(expectedValue)} but got ${stringifyObserved(actualValue)}.`
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
67
118
|
function evaluateFactExpectations(expectedFacts, observation) {
|
|
68
119
|
if (!expectedFacts || typeof expectedFacts !== 'object' || Array.isArray(expectedFacts)) {
|
|
69
120
|
return [];
|
|
70
121
|
}
|
|
71
122
|
return Object.entries(expectedFacts).map(([factPath, expectedValue]) => {
|
|
72
|
-
|
|
73
|
-
const passed = JSON.stringify(actualValue) === JSON.stringify(expectedValue);
|
|
74
|
-
return {
|
|
75
|
-
id: `expected.facts:${factPath}`,
|
|
76
|
-
passed,
|
|
77
|
-
message: passed
|
|
78
|
-
? `Fact "${factPath}" matched expected value.`
|
|
79
|
-
: `Fact "${factPath}" expected ${JSON.stringify(expectedValue)} but got ${JSON.stringify(actualValue)}.`
|
|
80
|
-
};
|
|
123
|
+
return evaluateOneFact(factPath, expectedValue, observation);
|
|
81
124
|
});
|
|
82
125
|
}
|
|
83
126
|
|
|
@@ -86,6 +129,11 @@ function evaluateProhibitedExpectations(prohibited, observation) {
|
|
|
86
129
|
return [];
|
|
87
130
|
}
|
|
88
131
|
return prohibited.map((factPath) => {
|
|
132
|
+
const parts = splitDottedPath(factPath);
|
|
133
|
+
const safety = factPathSafetyMessage(parts);
|
|
134
|
+
if (safety) {
|
|
135
|
+
return { id: `expected.prohibited:${factPath}`, passed: false, message: safety };
|
|
136
|
+
}
|
|
89
137
|
const actualValue = getValueAtPath(observation, factPath);
|
|
90
138
|
const passed = actualValue === undefined || actualValue === null || actualValue === false;
|
|
91
139
|
return {
|
|
@@ -93,7 +141,7 @@ function evaluateProhibitedExpectations(prohibited, observation) {
|
|
|
93
141
|
passed,
|
|
94
142
|
message: passed
|
|
95
143
|
? `Prohibited marker "${factPath}" was not observed.`
|
|
96
|
-
: `Prohibited marker "${factPath}" observed: ${
|
|
144
|
+
: `Prohibited marker "${factPath}" observed: ${stringifyObserved(actualValue)}.`
|
|
97
145
|
};
|
|
98
146
|
});
|
|
99
147
|
}
|
|
@@ -152,10 +200,74 @@ function softWrapFailedAssertion(assertion, soft) {
|
|
|
152
200
|
return softWrapLifecycleAssertion(assertion, soft);
|
|
153
201
|
}
|
|
154
202
|
|
|
155
|
-
function
|
|
203
|
+
function collectSoftModifierRows(expected) {
|
|
204
|
+
const rows = [];
|
|
205
|
+
if (Object.prototype.hasOwnProperty.call(expected, 'soft')) {
|
|
206
|
+
rows.push(
|
|
207
|
+
notesRow(
|
|
208
|
+
'expected.soft',
|
|
209
|
+
expected.soft === true
|
|
210
|
+
? 'soft: true — failures become warnings'
|
|
211
|
+
: 'soft: false — failures are blocking'
|
|
212
|
+
)
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
const notes = Array.isArray(expected.softNotes) ? expected.softNotes : [];
|
|
216
|
+
notes.forEach((item, index) => {
|
|
217
|
+
const text = typeof item === 'string'
|
|
218
|
+
? item
|
|
219
|
+
: String((item && (item.description || item.note)) || JSON.stringify(item));
|
|
220
|
+
rows.push(notesRow(`expected.softNotes[${index}]`, text));
|
|
221
|
+
});
|
|
222
|
+
return rows;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function collectEvidenceContextRows(spec, prefix) {
|
|
226
|
+
if (!spec || typeof spec !== 'object' || Array.isArray(spec)) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
const rows = [];
|
|
230
|
+
if (spec.roleKey) {
|
|
231
|
+
rows.push(notesRow(`${prefix}.roleKey`, `Evidence roleKey "${spec.roleKey}" (preflight context).`));
|
|
232
|
+
}
|
|
233
|
+
if (spec.resourceType) {
|
|
234
|
+
rows.push(
|
|
235
|
+
notesRow(`${prefix}.resourceType`, `Evidence resourceType "${spec.resourceType}" (preflight context).`)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
if (spec.requireStatus) {
|
|
239
|
+
rows.push(
|
|
240
|
+
notesRow(`${prefix}.requireStatus`, `Evidence requireStatus "${spec.requireStatus}" (preflight context).`)
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return rows;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function collectCapabilityAssertions(capabilities, observedCapabilities, soft) {
|
|
247
|
+
const spec = capabilities && typeof capabilities === 'object' ? capabilities : {};
|
|
248
|
+
const rows = [
|
|
249
|
+
...evaluateCapabilityExpectations('required', spec.required, observedCapabilities),
|
|
250
|
+
...evaluateCapabilityExpectations('allowed', spec.allowed, observedCapabilities),
|
|
251
|
+
...evaluateForbiddenCapabilities(spec.forbidden, observedCapabilities)
|
|
252
|
+
];
|
|
253
|
+
return rows.map((row) => softWrapFailedAssertion(row, soft));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function evaluateExecutionIntegrity(observation) {
|
|
257
|
+
if (!observation.workResultError) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
id: 'execution.integrity',
|
|
262
|
+
passed: false,
|
|
263
|
+
message: `Execution did not produce a readable result: ${observation.workResultError}`
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function collectCaseAssertions(expected, observation, preflight = {}) {
|
|
156
268
|
const soft = expected.soft === true;
|
|
157
269
|
const observedCapabilities = observation.capabilities?.observed || [];
|
|
158
|
-
const assertions = [];
|
|
270
|
+
const assertions = [evaluateExecutionIntegrity(observation)].filter(Boolean);
|
|
159
271
|
const lifecycleAssertion = softWrapFailedAssertion(
|
|
160
272
|
evaluateLifecycleStateExpectation(expected.lifecycleState, observation.lifecycleState),
|
|
161
273
|
soft
|
|
@@ -163,31 +275,35 @@ function collectCaseAssertions(expected, observation) {
|
|
|
163
275
|
if (lifecycleAssertion) {
|
|
164
276
|
assertions.push(lifecycleAssertion);
|
|
165
277
|
}
|
|
278
|
+
assertions.push(...collectSoftModifierRows(expected));
|
|
279
|
+
assertions.push(...collectCapabilityAssertions(expected.capabilities, observedCapabilities, soft));
|
|
166
280
|
assertions.push(
|
|
167
|
-
...
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
).map((row) => softWrapFailedAssertion(row, soft))
|
|
281
|
+
...evaluateFactExpectations(expected.facts, observation).map((row) =>
|
|
282
|
+
softWrapFailedAssertion(row, soft)
|
|
283
|
+
)
|
|
171
284
|
);
|
|
172
285
|
assertions.push(
|
|
173
|
-
...
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
).map((row) => softWrapFailedAssertion(row, soft))
|
|
286
|
+
...evaluateProhibitedExpectations(expected.prohibited, observation).map((row) =>
|
|
287
|
+
softWrapFailedAssertion(row, soft)
|
|
288
|
+
)
|
|
177
289
|
);
|
|
178
|
-
assertions.push(...evaluateFactExpectations(expected.facts, observation));
|
|
179
|
-
assertions.push(...evaluateProhibitedExpectations(expected.prohibited, observation));
|
|
180
290
|
assertions.push(...evaluateHonestyExpectations(expected.honesty, observation, { soft }));
|
|
181
291
|
assertions.push(...collectBidAssertions(expected.bid, observation, { soft }));
|
|
182
292
|
assertions.push(...evaluateEvidenceExpectations(expected.evidence, observation));
|
|
293
|
+
assertions.push(...collectEvidenceContextRows(expected.evidence, 'expected.evidence'));
|
|
294
|
+
assertions.push(...collectEvidenceContextRows(preflight.evidence, 'preflight.evidence'));
|
|
183
295
|
assertions.push(...evaluateSearchExpectations(expected.search, observation));
|
|
184
296
|
assertions.push(...evaluateSkillsExpectations(expected.skills, observation));
|
|
185
|
-
return assertions;
|
|
297
|
+
return assertions.map(withKind);
|
|
186
298
|
}
|
|
187
299
|
|
|
188
300
|
function evaluateRoleAssistantTestCase(testCase, result, lifecycleVerdict, portalUrl) {
|
|
189
301
|
const observation = buildCaseObservation(result, lifecycleVerdict, portalUrl);
|
|
190
|
-
const assertions = collectCaseAssertions(
|
|
302
|
+
const assertions = collectCaseAssertions(
|
|
303
|
+
testCase.expected || {},
|
|
304
|
+
observation,
|
|
305
|
+
testCase.preflight || {}
|
|
306
|
+
);
|
|
191
307
|
const failed = assertions.filter((row) => !row.passed);
|
|
192
308
|
return {
|
|
193
309
|
caseId: testCase.caseId,
|
|
@@ -202,5 +318,7 @@ function evaluateRoleAssistantTestCase(testCase, result, lifecycleVerdict, porta
|
|
|
202
318
|
}
|
|
203
319
|
|
|
204
320
|
module.exports = {
|
|
205
|
-
evaluateRoleAssistantTestCase
|
|
321
|
+
evaluateRoleAssistantTestCase,
|
|
322
|
+
evaluateFactExpectations,
|
|
323
|
+
getValueAtPath
|
|
206
324
|
};
|
|
@@ -55,6 +55,39 @@ function isActiveStatus(status, requiredStatus) {
|
|
|
55
55
|
return status === requiredStatus;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Dataplane Evidence is append-new versioned, never mutated in place
|
|
60
|
+
* (dataplane 506.2): once a row's content changes, its literal `key` is
|
|
61
|
+
* archived permanently and a new `<key>__vN` row becomes the current
|
|
62
|
+
* identity. A case authored against the original literal key must still
|
|
63
|
+
* resolve against that current versioned successor, not report it missing.
|
|
64
|
+
* `rows` here are already scoped to the case's roleKey+resourceType+status
|
|
65
|
+
* by `loadLiveEvidenceRows`, so any row whose key is exactly `<desired>__vN`
|
|
66
|
+
* is unambiguously that key's current version.
|
|
67
|
+
*/
|
|
68
|
+
function findVersionedSuccessor(desiredKey, byKey) {
|
|
69
|
+
const prefix = `${desiredKey}__v`;
|
|
70
|
+
let best = null;
|
|
71
|
+
let bestVersion = -1;
|
|
72
|
+
for (const [key, row] of byKey) {
|
|
73
|
+
if (!key.startsWith(prefix)) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
const suffix = key.slice(prefix.length);
|
|
77
|
+
const version = Number(suffix);
|
|
78
|
+
if (!Number.isInteger(version) || version <= bestVersion) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
best = row;
|
|
82
|
+
bestVersion = version;
|
|
83
|
+
}
|
|
84
|
+
return best;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function resolveEvidenceRow(desiredKey, byKey) {
|
|
88
|
+
return byKey.get(desiredKey) || findVersionedSuccessor(desiredKey, byKey);
|
|
89
|
+
}
|
|
90
|
+
|
|
58
91
|
function findMissingEvidenceKeys(spec, rows) {
|
|
59
92
|
const byKey = new Map();
|
|
60
93
|
for (const row of rows) {
|
|
@@ -65,7 +98,7 @@ function findMissingEvidenceKeys(spec, rows) {
|
|
|
65
98
|
}
|
|
66
99
|
const missing = [];
|
|
67
100
|
for (const key of spec.requiredKeys) {
|
|
68
|
-
const row =
|
|
101
|
+
const row = resolveEvidenceRow(key, byKey);
|
|
69
102
|
if (!row) {
|
|
70
103
|
missing.push(`${key} (not in dataplane)`);
|
|
71
104
|
continue;
|
|
@@ -133,11 +166,15 @@ function evaluateEvidenceExpectations(evidenceSpec, observation) {
|
|
|
133
166
|
return [];
|
|
134
167
|
}
|
|
135
168
|
const preflight = observation?.evidencePreflight || {};
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
169
|
+
const liveByKey = new Map();
|
|
170
|
+
for (const row of Array.isArray(preflight.rows) ? preflight.rows : []) {
|
|
171
|
+
const key = rowKey(row);
|
|
172
|
+
if (key) {
|
|
173
|
+
liveByKey.set(key, row);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
139
176
|
return requiredKeys.map((key) => {
|
|
140
|
-
const passed =
|
|
177
|
+
const passed = Boolean(resolveEvidenceRow(key, liveByKey));
|
|
141
178
|
return {
|
|
142
179
|
id: `expected.evidence.requiredKeys:${key}`,
|
|
143
180
|
passed,
|
|
@@ -168,6 +205,8 @@ function evaluateForbiddenCapabilities(forbiddenList, observedCapabilities) {
|
|
|
168
205
|
|
|
169
206
|
module.exports = {
|
|
170
207
|
evidenceSpecFromCase,
|
|
208
|
+
findVersionedSuccessor,
|
|
209
|
+
resolveEvidenceRow,
|
|
171
210
|
findMissingEvidenceKeys,
|
|
172
211
|
assertRoleAssistantEvidencePreflight,
|
|
173
212
|
evaluateEvidenceExpectations,
|