@ryuenn3123/agentic-senior-core 3.0.50 → 4.0.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.
- package/.agent-context/review-checklists/pr-checklist.md +1 -0
- package/.agent-context/rules/api-docs.md +63 -47
- package/.agent-context/rules/architecture.md +133 -120
- package/.agent-context/rules/database-design.md +36 -18
- package/.agent-context/rules/docker-runtime.md +66 -43
- package/.agent-context/rules/efficiency-vs-hype.md +38 -17
- package/.agent-context/rules/error-handling.md +35 -16
- package/.agent-context/rules/event-driven.md +35 -18
- package/.agent-context/rules/frontend-architecture.md +103 -76
- package/.agent-context/rules/git-workflow.md +81 -197
- package/.agent-context/rules/microservices.md +42 -41
- package/.agent-context/rules/naming-conv.md +27 -8
- package/.agent-context/rules/performance.md +32 -12
- package/.agent-context/rules/realtime.md +26 -9
- package/.agent-context/rules/security.md +39 -20
- package/.agent-context/rules/testing.md +36 -16
- package/AGENTS.md +9 -9
- package/README.md +10 -1
- package/lib/cli/commands/init.mjs +1 -0
- package/lib/cli/compiler.mjs +1 -0
- package/lib/cli/detector/constants.mjs +135 -0
- package/lib/cli/detector/design-evidence/collector.mjs +256 -0
- package/lib/cli/detector/design-evidence/constants.mjs +39 -0
- package/lib/cli/detector/design-evidence/file-traversal.mjs +83 -0
- package/lib/cli/detector/design-evidence/structured-attribute-evidence.mjs +117 -0
- package/lib/cli/detector/design-evidence/summary.mjs +109 -0
- package/lib/cli/detector/design-evidence/utility-helpers.mjs +122 -0
- package/lib/cli/detector/design-evidence.mjs +25 -610
- package/lib/cli/detector/stack-detection.mjs +243 -0
- package/lib/cli/detector/ui-signals.mjs +150 -0
- package/lib/cli/detector/workspace-scan.mjs +177 -0
- package/lib/cli/detector.mjs +20 -688
- package/lib/cli/memory-continuity.mjs +1 -0
- package/lib/cli/project-scaffolder/design-contract/sections/audits.mjs +96 -0
- package/lib/cli/project-scaffolder/design-contract/sections/conceptual-anchor.mjs +116 -0
- package/lib/cli/project-scaffolder/design-contract/sections/execution-handoff.mjs +211 -0
- package/lib/cli/project-scaffolder/design-contract/seed-signals.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/signal-vocab.mjs +64 -0
- package/lib/cli/project-scaffolder/design-contract/validation/anchor-validators.mjs +222 -0
- package/lib/cli/project-scaffolder/design-contract/validation/audit-validators.mjs +117 -0
- package/lib/cli/project-scaffolder/design-contract/validation/completeness.mjs +83 -0
- package/lib/cli/project-scaffolder/design-contract/validation/execution-validators.mjs +328 -0
- package/lib/cli/project-scaffolder/design-contract/validation/helpers.mjs +8 -0
- package/lib/cli/project-scaffolder/design-contract/validation/structural-validators.mjs +79 -0
- package/lib/cli/project-scaffolder/design-contract/validation/system-validators.mjs +256 -0
- package/lib/cli/project-scaffolder/design-contract/validation.mjs +59 -896
- package/lib/cli/project-scaffolder/design-contract.mjs +147 -557
- package/mcp.json +30 -9
- package/package.json +17 -2
- package/scripts/audit-cache-layer-contract.mjs +258 -0
- package/scripts/audit-caching-scope-hygiene.mjs +263 -0
- package/scripts/audit-file-size.mjs +219 -0
- package/scripts/audit-reflection-citations.mjs +163 -0
- package/scripts/audit-release-bundle.mjs +170 -0
- package/scripts/audit-rule-id-uniqueness.mjs +313 -0
- package/scripts/benchmark-evidence-bundle.mjs +1 -0
- package/scripts/build-release-benchmark-bundle.mjs +204 -0
- package/scripts/context-triggered-audit.mjs +1 -0
- package/scripts/documentation-boundary-audit.mjs +1 -0
- package/scripts/explain-on-demand-audit.mjs +2 -1
- package/scripts/frontend-usability-audit.mjs +10 -10
- package/scripts/llm-judge/checklist-loader.mjs +45 -0
- package/scripts/llm-judge/constants.mjs +66 -0
- package/scripts/llm-judge/diff-collection.mjs +74 -0
- package/scripts/llm-judge/prompting.mjs +78 -0
- package/scripts/llm-judge/providers.mjs +111 -0
- package/scripts/llm-judge/verdict.mjs +134 -0
- package/scripts/llm-judge.mjs +21 -482
- package/scripts/mcp-server/tool-registry.mjs +55 -0
- package/scripts/mcp-server/tools.mjs +137 -1
- package/scripts/migrate-rule-format/id-prefix-table.mjs +37 -0
- package/scripts/migrate-rule-format/parse-legacy.mjs +180 -0
- package/scripts/migrate-rule-format/render-new.mjs +169 -0
- package/scripts/migrate-rule-format/roundtrip-validate.mjs +89 -0
- package/scripts/migrate-rule-format.mjs +192 -0
- package/scripts/release-gate/constants.mjs +1 -1
- package/scripts/release-gate/static-checks.mjs +1 -1
- package/scripts/rules-guardian-audit.mjs +5 -2
- package/scripts/single-source-lazy-loading-audit.mjs +2 -1
- package/scripts/ui-design-judge/git-input.mjs +3 -0
- package/scripts/validate/config.mjs +3 -2
- package/scripts/validate/coverage-checks.mjs +1 -1
- package/scripts/validate.mjs +93 -1
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validators for the design execution policy, the structured execution
|
|
3
|
+
* handoff, the review rubric, the required design sections list, and the
|
|
4
|
+
* forbidden patterns list. These collectively gate the structured handoff
|
|
5
|
+
* payload that downstream UI judges and review tooling consume.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { hasNonEmptyString } from './helpers.mjs';
|
|
9
|
+
import { DESIGN_REQUIRED_SECTIONS } from '../../constants.mjs';
|
|
10
|
+
|
|
11
|
+
export function validateDesignExecutionPolicy(designIntentContract, validationErrors) {
|
|
12
|
+
if (!designIntentContract.designExecutionPolicy || typeof designIntentContract.designExecutionPolicy !== 'object') {
|
|
13
|
+
validationErrors.push('designIntent.designExecutionPolicy must exist.');
|
|
14
|
+
return validationErrors;
|
|
15
|
+
}
|
|
16
|
+
const designExecutionPolicy = designIntentContract.designExecutionPolicy;
|
|
17
|
+
if (designExecutionPolicy.representationStrategy !== 'surface-plan-v1') {
|
|
18
|
+
validationErrors.push('designIntent.designExecutionPolicy.representationStrategy must equal "surface-plan-v1".');
|
|
19
|
+
}
|
|
20
|
+
for (const requiredFlagName of [
|
|
21
|
+
'requireSurfacePlan',
|
|
22
|
+
'requireComponentGraph',
|
|
23
|
+
'requireViewportMutationPlan',
|
|
24
|
+
'requireInteractionStateMatrix',
|
|
25
|
+
'requireContentPriorityMap',
|
|
26
|
+
'requireTaskFlowNarrative',
|
|
27
|
+
'requireSignatureMoveRationale',
|
|
28
|
+
'requireCreativeCommitmentGate',
|
|
29
|
+
'requireStructuredHandoff',
|
|
30
|
+
'requireRepoEvidenceAlignment',
|
|
31
|
+
'forbidScreenshotDependency',
|
|
32
|
+
'separateRequiredOutcomesFromCandidateMoves',
|
|
33
|
+
'forbidCandidateMovesAsLockedRequirements',
|
|
34
|
+
'forbidLibraryThemeAsVisualAuthority',
|
|
35
|
+
'forbidLiteralAnchorChromeWithoutProductFunction',
|
|
36
|
+
'requirePerSurfaceMutationOps',
|
|
37
|
+
'forbidUniformSiblingSurfaceTreatment',
|
|
38
|
+
'zeroBasedRedesignResetsPriorVisualsWhenRequested',
|
|
39
|
+
]) {
|
|
40
|
+
if (designExecutionPolicy[requiredFlagName] !== true) {
|
|
41
|
+
validationErrors.push(`designIntent.designExecutionPolicy.${requiredFlagName} must equal true.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (designExecutionPolicy.handoffFormatVersion !== 'ui-handoff-v1') {
|
|
45
|
+
validationErrors.push('designIntent.designExecutionPolicy.handoffFormatVersion must equal "ui-handoff-v1".');
|
|
46
|
+
}
|
|
47
|
+
if (
|
|
48
|
+
!Array.isArray(designExecutionPolicy.semanticReviewFocus)
|
|
49
|
+
|| designExecutionPolicy.semanticReviewFocus.length < 4
|
|
50
|
+
) {
|
|
51
|
+
validationErrors.push('designIntent.designExecutionPolicy.semanticReviewFocus must list the required review dimensions.');
|
|
52
|
+
}
|
|
53
|
+
return validationErrors;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function validateHandoffComponentGraph(designExecutionHandoff, validationErrors) {
|
|
57
|
+
const componentGraph = designExecutionHandoff.componentGraph;
|
|
58
|
+
if (!componentGraph || typeof componentGraph !== 'object') {
|
|
59
|
+
validationErrors.push('designIntent.designExecutionHandoff.componentGraph must exist.');
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!Array.isArray(componentGraph.nodes) || componentGraph.nodes.length < 2) {
|
|
63
|
+
validationErrors.push('designIntent.designExecutionHandoff.componentGraph.nodes must list the primary execution nodes.');
|
|
64
|
+
}
|
|
65
|
+
if (!Array.isArray(componentGraph.edges) || componentGraph.edges.length < 1) {
|
|
66
|
+
validationErrors.push('designIntent.designExecutionHandoff.componentGraph.edges must define relationships between UI nodes.');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateHandoffContentPriorityMap(designExecutionHandoff, validationErrors) {
|
|
71
|
+
const contentPriorityMap = designExecutionHandoff.contentPriorityMap;
|
|
72
|
+
if (!contentPriorityMap || typeof contentPriorityMap !== 'object') {
|
|
73
|
+
validationErrors.push('designIntent.designExecutionHandoff.contentPriorityMap must exist.');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const priorityBucket of ['primary', 'secondary', 'deferred']) {
|
|
77
|
+
if (!Array.isArray(contentPriorityMap[priorityBucket]) || contentPriorityMap[priorityBucket].length < 1) {
|
|
78
|
+
validationErrors.push(`designIntent.designExecutionHandoff.contentPriorityMap.${priorityBucket} must contain at least one item.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validateHandoffViewportMutationPlan(designExecutionHandoff, validationErrors) {
|
|
84
|
+
const viewportMutationPlan = designExecutionHandoff.viewportMutationPlan;
|
|
85
|
+
if (!viewportMutationPlan || typeof viewportMutationPlan !== 'object') {
|
|
86
|
+
validationErrors.push('designIntent.designExecutionHandoff.viewportMutationPlan must exist.');
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
for (const viewportKey of ['mobile', 'tablet', 'desktop']) {
|
|
90
|
+
const viewportPlan = viewportMutationPlan[viewportKey];
|
|
91
|
+
if (!viewportPlan || typeof viewportPlan !== 'object') {
|
|
92
|
+
validationErrors.push(`designIntent.designExecutionHandoff.viewportMutationPlan.${viewportKey} must be an object.`);
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!String(viewportPlan.primaryOperation || '').trim()) {
|
|
96
|
+
validationErrors.push(`designIntent.designExecutionHandoff.viewportMutationPlan.${viewportKey}.primaryOperation must be a non-empty string.`);
|
|
97
|
+
}
|
|
98
|
+
if (!Array.isArray(viewportPlan.requiredSurfaceActions) || viewportPlan.requiredSurfaceActions.length < 2) {
|
|
99
|
+
validationErrors.push(`designIntent.designExecutionHandoff.viewportMutationPlan.${viewportKey}.requiredSurfaceActions must contain at least two actions.`);
|
|
100
|
+
}
|
|
101
|
+
if (!Array.isArray(viewportPlan.forbiddenPatterns) || viewportPlan.forbiddenPatterns.length < 1) {
|
|
102
|
+
validationErrors.push(`designIntent.designExecutionHandoff.viewportMutationPlan.${viewportKey}.forbiddenPatterns must contain at least one anti-pattern.`);
|
|
103
|
+
}
|
|
104
|
+
if (!String(viewportPlan.rationale || '').trim()) {
|
|
105
|
+
validationErrors.push(`designIntent.designExecutionHandoff.viewportMutationPlan.${viewportKey}.rationale must be a non-empty string.`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function validateHandoffExpressionFlexibility(designExecutionHandoff, validationErrors) {
|
|
111
|
+
const expressionFlexibility = designExecutionHandoff.expressionFlexibility;
|
|
112
|
+
if (!expressionFlexibility || typeof expressionFlexibility !== 'object') {
|
|
113
|
+
validationErrors.push('designIntent.designExecutionHandoff.expressionFlexibility must exist.');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!Array.isArray(expressionFlexibility.lockedOutcomes) || expressionFlexibility.lockedOutcomes.length < 3) {
|
|
117
|
+
validationErrors.push('designIntent.designExecutionHandoff.expressionFlexibility.lockedOutcomes must list locked outcomes.');
|
|
118
|
+
}
|
|
119
|
+
if (!Array.isArray(expressionFlexibility.candidateSignatureMoves) || expressionFlexibility.candidateSignatureMoves.length < 1) {
|
|
120
|
+
validationErrors.push('designIntent.designExecutionHandoff.expressionFlexibility.candidateSignatureMoves must include at least one candidate move placeholder.');
|
|
121
|
+
}
|
|
122
|
+
if (!Array.isArray(expressionFlexibility.flexibleAxes) || expressionFlexibility.flexibleAxes.length < 4) {
|
|
123
|
+
validationErrors.push('designIntent.designExecutionHandoff.expressionFlexibility.flexibleAxes must list flexible implementation axes.');
|
|
124
|
+
}
|
|
125
|
+
if (!String(expressionFlexibility.lockingRule || '').includes('candidate')) {
|
|
126
|
+
validationErrors.push('designIntent.designExecutionHandoff.expressionFlexibility.lockingRule must explain when candidate moves become required.');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function validateHandoffCreativeCommitment(designExecutionHandoff, validationErrors) {
|
|
131
|
+
const creativeCommitment = designExecutionHandoff.creativeCommitment;
|
|
132
|
+
if (!creativeCommitment || typeof creativeCommitment !== 'object') {
|
|
133
|
+
validationErrors.push('designIntent.designExecutionHandoff.creativeCommitment must exist.');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (creativeCommitment.status !== 'agent-must-complete-before-ui-implementation') {
|
|
137
|
+
validationErrors.push('designIntent.designExecutionHandoff.creativeCommitment.status must equal "agent-must-complete-before-ui-implementation".');
|
|
138
|
+
}
|
|
139
|
+
if (
|
|
140
|
+
!Array.isArray(creativeCommitment.requiredFields)
|
|
141
|
+
|| !creativeCommitment.requiredFields.includes('specificReferencePoint')
|
|
142
|
+
|| !creativeCommitment.requiredFields.includes('signatureMotion')
|
|
143
|
+
|| !creativeCommitment.requiredFields.includes('typographicDecision')
|
|
144
|
+
) {
|
|
145
|
+
validationErrors.push('designIntent.designExecutionHandoff.creativeCommitment.requiredFields must include specificReferencePoint, signatureMotion, and typographicDecision.');
|
|
146
|
+
}
|
|
147
|
+
if (!hasNonEmptyString(creativeCommitment.failureMode)) {
|
|
148
|
+
validationErrors.push('designIntent.designExecutionHandoff.creativeCommitment.failureMode must be a non-empty string.');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function validateHandoffImplementationGuardrails(designExecutionHandoff, validationErrors) {
|
|
153
|
+
const implementationGuardrails = designExecutionHandoff.implementationGuardrails;
|
|
154
|
+
if (!implementationGuardrails || typeof implementationGuardrails !== 'object') {
|
|
155
|
+
validationErrors.push('designIntent.designExecutionHandoff.implementationGuardrails must exist.');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
for (const requiredFlagName of [
|
|
159
|
+
'requireBuildFromHandoff',
|
|
160
|
+
'requireGapNotesBeforeFallback',
|
|
161
|
+
'forbidGenericLayoutFallbackWithoutReason',
|
|
162
|
+
'requireLockedVsFlexibleDecisionReview',
|
|
163
|
+
'forbidCandidateMoveHardcoding',
|
|
164
|
+
'forbidTestingDemoCopyInUi',
|
|
165
|
+
'forbidTerminalOnlyUserFlows',
|
|
166
|
+
]) {
|
|
167
|
+
if (implementationGuardrails[requiredFlagName] !== true) {
|
|
168
|
+
validationErrors.push(`designIntent.designExecutionHandoff.implementationGuardrails.${requiredFlagName} must equal true.`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function validateDesignExecutionHandoff(designIntentContract, validationErrors) {
|
|
174
|
+
if (!designIntentContract.designExecutionHandoff || typeof designIntentContract.designExecutionHandoff !== 'object') {
|
|
175
|
+
validationErrors.push('designIntent.designExecutionHandoff must exist.');
|
|
176
|
+
return validationErrors;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const designExecutionHandoff = designIntentContract.designExecutionHandoff;
|
|
180
|
+
if (designExecutionHandoff.version !== 'ui-handoff-v1') {
|
|
181
|
+
validationErrors.push('designIntent.designExecutionHandoff.version must equal "ui-handoff-v1".');
|
|
182
|
+
}
|
|
183
|
+
if (designExecutionHandoff.seedMode !== 'structure-first-scaffold') {
|
|
184
|
+
validationErrors.push('designIntent.designExecutionHandoff.seedMode must equal "structure-first-scaffold".');
|
|
185
|
+
}
|
|
186
|
+
if (designExecutionHandoff.requiresTaskSpecificRefinement !== true) {
|
|
187
|
+
validationErrors.push('designIntent.designExecutionHandoff.requiresTaskSpecificRefinement must equal true.');
|
|
188
|
+
}
|
|
189
|
+
if (!String(designExecutionHandoff.primaryExperienceGoal || '').trim()) {
|
|
190
|
+
validationErrors.push('designIntent.designExecutionHandoff.primaryExperienceGoal must be a non-empty string.');
|
|
191
|
+
}
|
|
192
|
+
if (!Array.isArray(designExecutionHandoff.surfacePlan) || designExecutionHandoff.surfacePlan.length < 1) {
|
|
193
|
+
validationErrors.push('designIntent.designExecutionHandoff.surfacePlan must define at least one planned surface.');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
validateHandoffComponentGraph(designExecutionHandoff, validationErrors);
|
|
197
|
+
validateHandoffContentPriorityMap(designExecutionHandoff, validationErrors);
|
|
198
|
+
validateHandoffViewportMutationPlan(designExecutionHandoff, validationErrors);
|
|
199
|
+
|
|
200
|
+
if (!Array.isArray(designExecutionHandoff.interactionStateMatrix) || designExecutionHandoff.interactionStateMatrix.length < 1) {
|
|
201
|
+
validationErrors.push('designIntent.designExecutionHandoff.interactionStateMatrix must list key component state expectations.');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
validateHandoffExpressionFlexibility(designExecutionHandoff, validationErrors);
|
|
205
|
+
|
|
206
|
+
if (!Array.isArray(designExecutionHandoff.taskFlowNarrative) || designExecutionHandoff.taskFlowNarrative.length < 2) {
|
|
207
|
+
validationErrors.push('designIntent.designExecutionHandoff.taskFlowNarrative must describe the key UI task flow in sequence.');
|
|
208
|
+
}
|
|
209
|
+
if (!String(designExecutionHandoff.signatureMoveRationale || '').trim()) {
|
|
210
|
+
validationErrors.push('designIntent.designExecutionHandoff.signatureMoveRationale must explain the chosen authored move.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
validateHandoffCreativeCommitment(designExecutionHandoff, validationErrors);
|
|
214
|
+
validateHandoffImplementationGuardrails(designExecutionHandoff, validationErrors);
|
|
215
|
+
return validationErrors;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function validateReviewRubric(designIntentContract, validationErrors) {
|
|
219
|
+
if (!designIntentContract.reviewRubric || typeof designIntentContract.reviewRubric !== 'object') {
|
|
220
|
+
validationErrors.push('designIntent.reviewRubric must exist.');
|
|
221
|
+
return validationErrors;
|
|
222
|
+
}
|
|
223
|
+
const reviewRubric = designIntentContract.reviewRubric;
|
|
224
|
+
if (reviewRubric.version !== 'ui-rubric-v1') {
|
|
225
|
+
validationErrors.push('designIntent.reviewRubric.version must equal "ui-rubric-v1".');
|
|
226
|
+
}
|
|
227
|
+
if (reviewRubric.genericityAutoFail !== true) {
|
|
228
|
+
validationErrors.push('designIntent.reviewRubric.genericityAutoFail must equal true.');
|
|
229
|
+
}
|
|
230
|
+
if (!Array.isArray(reviewRubric.dimensions) || reviewRubric.dimensions.length < 5) {
|
|
231
|
+
validationErrors.push('designIntent.reviewRubric.dimensions must define the required rubric dimensions.');
|
|
232
|
+
} else {
|
|
233
|
+
for (const requiredRubricKey of [
|
|
234
|
+
'distinctiveness',
|
|
235
|
+
'contractFidelity',
|
|
236
|
+
'visualConsistency',
|
|
237
|
+
'heuristicUxQuality',
|
|
238
|
+
'motionDiscipline',
|
|
239
|
+
]) {
|
|
240
|
+
if (!reviewRubric.dimensions.some((dimension) => dimension?.key === requiredRubricKey)) {
|
|
241
|
+
validationErrors.push(`designIntent.reviewRubric.dimensions is missing "${requiredRubricKey}".`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (!Array.isArray(reviewRubric.genericitySignals) || reviewRubric.genericitySignals.length < 3) {
|
|
246
|
+
validationErrors.push('designIntent.reviewRubric.genericitySignals must list common genericity drift signals.');
|
|
247
|
+
} else {
|
|
248
|
+
for (const requiredSignal of [
|
|
249
|
+
'ai-safe-ui-template-look',
|
|
250
|
+
'ai-color-default-palette-without-product-role-behavior',
|
|
251
|
+
'interchangeable-product-renaming-test-fails',
|
|
252
|
+
'decorative-grid-or-glow-wallpaper-without-product-function',
|
|
253
|
+
'decorative-line-or-calibration-wallpaper-without-product-function',
|
|
254
|
+
'measurement-or-calibration-marks-used-as-page-background',
|
|
255
|
+
'testing-demo-or-placeholder-copy-shipped-to-ui',
|
|
256
|
+
'terminal-only-user-flow-without-product-reason',
|
|
257
|
+
'motion-or-3d-omitted-from-fear-without-fit-analysis',
|
|
258
|
+
]) {
|
|
259
|
+
if (!reviewRubric.genericitySignals.includes(requiredSignal)) {
|
|
260
|
+
validationErrors.push(`designIntent.reviewRubric.genericitySignals must include "${requiredSignal}".`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (!Array.isArray(reviewRubric.validBoldSignals) || reviewRubric.validBoldSignals.length < 3) {
|
|
265
|
+
validationErrors.push('designIntent.reviewRubric.validBoldSignals must list legitimate authored signals.');
|
|
266
|
+
} else {
|
|
267
|
+
for (const requiredSignal of [
|
|
268
|
+
'three-at-a-glance-product-specific-signals',
|
|
269
|
+
'visually-exploratory-accessible-palette-derived-from-product',
|
|
270
|
+
'audacious-accessible-palette-with-product-role-behavior',
|
|
271
|
+
'motion-or-spatial-experience-derived-from-anchor',
|
|
272
|
+
]) {
|
|
273
|
+
if (!reviewRubric.validBoldSignals.includes(requiredSignal)) {
|
|
274
|
+
validationErrors.push(`designIntent.reviewRubric.validBoldSignals must include "${requiredSignal}".`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!reviewRubric.reportingRules || typeof reviewRubric.reportingRules !== 'object') {
|
|
279
|
+
validationErrors.push('designIntent.reviewRubric.reportingRules must exist.');
|
|
280
|
+
return validationErrors;
|
|
281
|
+
}
|
|
282
|
+
for (const requiredFlagName of [
|
|
283
|
+
'mustExplainGenericity',
|
|
284
|
+
'mustSeparateTasteFromFailure',
|
|
285
|
+
'contractFidelityOverridesPersonalTaste',
|
|
286
|
+
]) {
|
|
287
|
+
if (reviewRubric.reportingRules[requiredFlagName] !== true) {
|
|
288
|
+
validationErrors.push(`designIntent.reviewRubric.reportingRules.${requiredFlagName} must equal true.`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return validationErrors;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function validateRequiredSectionsAndForbiddenPatterns(designIntentContract, validationErrors) {
|
|
295
|
+
if (
|
|
296
|
+
!Array.isArray(designIntentContract.requiredDesignSections)
|
|
297
|
+
|| designIntentContract.requiredDesignSections.length !== DESIGN_REQUIRED_SECTIONS.length
|
|
298
|
+
) {
|
|
299
|
+
validationErrors.push('designIntent.requiredDesignSections must match the required design contract sections.');
|
|
300
|
+
} else {
|
|
301
|
+
for (const requiredSectionName of DESIGN_REQUIRED_SECTIONS) {
|
|
302
|
+
if (!designIntentContract.requiredDesignSections.includes(requiredSectionName)) {
|
|
303
|
+
validationErrors.push(`designIntent.requiredDesignSections is missing "${requiredSectionName}".`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (!Array.isArray(designIntentContract.forbiddenPatterns) || designIntentContract.forbiddenPatterns.length < 4) {
|
|
309
|
+
validationErrors.push('designIntent.forbiddenPatterns must list concrete anti-generic patterns.');
|
|
310
|
+
return validationErrors;
|
|
311
|
+
}
|
|
312
|
+
for (const requiredPattern of [
|
|
313
|
+
'ai-safe-ui-template-look',
|
|
314
|
+
'ai-color-default-palette-without-product-role-behavior',
|
|
315
|
+
'interchangeable-product-renaming-test-fails',
|
|
316
|
+
'decorative-grid-or-glow-wallpaper-without-product-function',
|
|
317
|
+
'decorative-line-or-calibration-wallpaper-without-product-function',
|
|
318
|
+
'measurement-or-calibration-marks-used-as-page-background',
|
|
319
|
+
'testing-demo-or-placeholder-copy-shipped-to-ui',
|
|
320
|
+
'terminal-only-user-flow-without-product-reason',
|
|
321
|
+
'motion-or-3d-omitted-from-fear-without-fit-analysis',
|
|
322
|
+
]) {
|
|
323
|
+
if (!designIntentContract.forbiddenPatterns.includes(requiredPattern)) {
|
|
324
|
+
validationErrors.push(`designIntent.forbiddenPatterns must include "${requiredPattern}".`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return validationErrors;
|
|
328
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural-shape validators for the design intent contract: top-level mode,
|
|
3
|
+
* project block, design philosophy, external research intake, and the design
|
|
4
|
+
* flexibility policy. Each validator pushes its findings onto the shared error
|
|
5
|
+
* accumulator and returns it.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { hasNonEmptyString } from './helpers.mjs';
|
|
9
|
+
|
|
10
|
+
export function validateModeAndProjectShape(designIntentContract, validationErrors) {
|
|
11
|
+
if (designIntentContract.mode !== 'dynamic') {
|
|
12
|
+
validationErrors.push('designIntent.mode must equal "dynamic".');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!designIntentContract.project || typeof designIntentContract.project !== 'object') {
|
|
16
|
+
validationErrors.push('designIntent.project must exist.');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (!designIntentContract.designPhilosophy || typeof designIntentContract.designPhilosophy !== 'string') {
|
|
20
|
+
validationErrors.push('designIntent.designPhilosophy must be a non-empty string.');
|
|
21
|
+
}
|
|
22
|
+
return validationErrors;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function validateExternalResearchIntake(designIntentContract, validationErrors) {
|
|
26
|
+
if (!designIntentContract.externalResearchIntake || typeof designIntentContract.externalResearchIntake !== 'object') {
|
|
27
|
+
validationErrors.push('designIntent.externalResearchIntake must exist.');
|
|
28
|
+
return validationErrors;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const externalResearchIntake = designIntentContract.externalResearchIntake;
|
|
32
|
+
if (externalResearchIntake.userSuppliedResearchPolicy !== 'read-as-candidate-evidence-not-final-prescription') {
|
|
33
|
+
validationErrors.push('designIntent.externalResearchIntake.userSuppliedResearchPolicy must preserve user research as candidate evidence.');
|
|
34
|
+
}
|
|
35
|
+
if (externalResearchIntake.requireOfficialDocsVerificationForTechnologyClaims !== true) {
|
|
36
|
+
validationErrors.push('designIntent.externalResearchIntake.requireOfficialDocsVerificationForTechnologyClaims must equal true.');
|
|
37
|
+
}
|
|
38
|
+
if (
|
|
39
|
+
!Array.isArray(externalResearchIntake.candidateDomains)
|
|
40
|
+
|| !externalResearchIntake.candidateDomains.includes('motion-and-scroll')
|
|
41
|
+
) {
|
|
42
|
+
validationErrors.push('designIntent.externalResearchIntake.candidateDomains must include motion-and-scroll.');
|
|
43
|
+
}
|
|
44
|
+
return validationErrors;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function validateDesignFlexibilityPolicy(designIntentContract, validationErrors) {
|
|
48
|
+
if (!designIntentContract.designFlexibilityPolicy || typeof designIntentContract.designFlexibilityPolicy !== 'object') {
|
|
49
|
+
validationErrors.push('designIntent.designFlexibilityPolicy must exist.');
|
|
50
|
+
return validationErrors;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const designFlexibilityPolicy = designIntentContract.designFlexibilityPolicy;
|
|
54
|
+
if (designFlexibilityPolicy.mode !== 'locked-outcomes-flexible-expression') {
|
|
55
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.mode must equal "locked-outcomes-flexible-expression".');
|
|
56
|
+
}
|
|
57
|
+
if (!hasNonEmptyString(designFlexibilityPolicy.contractRole)) {
|
|
58
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.contractRole must be a non-empty string.');
|
|
59
|
+
}
|
|
60
|
+
if (!Array.isArray(designFlexibilityPolicy.lockedOutcomeTypes) || designFlexibilityPolicy.lockedOutcomeTypes.length < 4) {
|
|
61
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.lockedOutcomeTypes must list the locked outcome categories.');
|
|
62
|
+
}
|
|
63
|
+
if (!Array.isArray(designFlexibilityPolicy.flexibleExpressionAxes) || designFlexibilityPolicy.flexibleExpressionAxes.length < 4) {
|
|
64
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.flexibleExpressionAxes must list flexible expression axes.');
|
|
65
|
+
}
|
|
66
|
+
if (!hasNonEmptyString(designFlexibilityPolicy.tokenLockingRule)) {
|
|
67
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.tokenLockingRule must be a non-empty string.');
|
|
68
|
+
}
|
|
69
|
+
if (!String(designFlexibilityPolicy.signatureMovePolicy || '').includes('candidate')) {
|
|
70
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.signatureMovePolicy must separate candidate moves from required outcomes.');
|
|
71
|
+
}
|
|
72
|
+
if (!String(designFlexibilityPolicy.libraryVisualLanguagePolicy || '').includes('Libraries supply')) {
|
|
73
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.libraryVisualLanguagePolicy must keep libraries from dictating visual language.');
|
|
74
|
+
}
|
|
75
|
+
if (!String(designFlexibilityPolicy.literalAnchorPolicy || '').includes('Translate anchors')) {
|
|
76
|
+
validationErrors.push('designIntent.designFlexibilityPolicy.literalAnchorPolicy must require non-literal anchor translation.');
|
|
77
|
+
}
|
|
78
|
+
return validationErrors;
|
|
79
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validators for token system, color truth, viewport adaptation, motion
|
|
3
|
+
* system, component morphology, accessibility policy, and context hygiene
|
|
4
|
+
* sections. These cover the structured systems that drive UI implementation.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { hasNonEmptyString } from './helpers.mjs';
|
|
8
|
+
|
|
9
|
+
function validateTokenLockingPolicy(tokenSystem, validationErrors) {
|
|
10
|
+
const tokenLockingPolicy = tokenSystem.tokenLockingPolicy;
|
|
11
|
+
if (!tokenLockingPolicy || typeof tokenLockingPolicy !== 'object') {
|
|
12
|
+
validationErrors.push('designIntent.tokenSystem.tokenLockingPolicy must exist.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (tokenLockingPolicy.defaultLockState !== 'semantic-roles-locked-primitives-flexible') {
|
|
16
|
+
validationErrors.push('designIntent.tokenSystem.tokenLockingPolicy.defaultLockState must preserve semantic roles while keeping primitives flexible.');
|
|
17
|
+
}
|
|
18
|
+
if (!Array.isArray(tokenLockingPolicy.flexibleByDefault) || tokenLockingPolicy.flexibleByDefault.length < 4) {
|
|
19
|
+
validationErrors.push('designIntent.tokenSystem.tokenLockingPolicy.flexibleByDefault must list flexible primitive axes.');
|
|
20
|
+
}
|
|
21
|
+
if (!hasNonEmptyString(tokenLockingPolicy.promotionRule)) {
|
|
22
|
+
validationErrors.push('designIntent.tokenSystem.tokenLockingPolicy.promotionRule must be a non-empty string.');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function validateTokenFallbackAndNaming(tokenSystem, validationErrors) {
|
|
27
|
+
const fallbackPolicy = tokenSystem.fallbackPolicy;
|
|
28
|
+
if (!fallbackPolicy || typeof fallbackPolicy !== 'object') {
|
|
29
|
+
validationErrors.push('designIntent.tokenSystem.fallbackPolicy must exist.');
|
|
30
|
+
} else {
|
|
31
|
+
if (fallbackPolicy.forbidRawHexOutsidePrimitives !== true) {
|
|
32
|
+
validationErrors.push('designIntent.tokenSystem.fallbackPolicy.forbidRawHexOutsidePrimitives must equal true.');
|
|
33
|
+
}
|
|
34
|
+
if (fallbackPolicy.forbidRawSpacingOutsidePrimitives !== true) {
|
|
35
|
+
validationErrors.push('designIntent.tokenSystem.fallbackPolicy.forbidRawSpacingOutsidePrimitives must equal true.');
|
|
36
|
+
}
|
|
37
|
+
if (fallbackPolicy.requireDocumentedExceptionForLegacyBypass !== true) {
|
|
38
|
+
validationErrors.push('designIntent.tokenSystem.fallbackPolicy.requireDocumentedExceptionForLegacyBypass must equal true.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const namingConstraints = tokenSystem.namingConstraints;
|
|
43
|
+
if (!namingConstraints || typeof namingConstraints !== 'object') {
|
|
44
|
+
validationErrors.push('designIntent.tokenSystem.namingConstraints must exist.');
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (namingConstraints.forbidCurlyBracesInNames !== true) {
|
|
48
|
+
validationErrors.push('designIntent.tokenSystem.namingConstraints.forbidCurlyBracesInNames must equal true.');
|
|
49
|
+
}
|
|
50
|
+
if (namingConstraints.forbidDotsInNames !== true) {
|
|
51
|
+
validationErrors.push('designIntent.tokenSystem.namingConstraints.forbidDotsInNames must equal true.');
|
|
52
|
+
}
|
|
53
|
+
if (namingConstraints.forbidSquareBracketsInNames !== true) {
|
|
54
|
+
validationErrors.push('designIntent.tokenSystem.namingConstraints.forbidSquareBracketsInNames must equal true.');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function validateTokenSystem(designIntentContract, validationErrors) {
|
|
59
|
+
if (!designIntentContract.tokenSystem || typeof designIntentContract.tokenSystem !== 'object') {
|
|
60
|
+
validationErrors.push('designIntent.tokenSystem must exist.');
|
|
61
|
+
return validationErrors;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const tokenSystem = designIntentContract.tokenSystem;
|
|
65
|
+
const taxonomyOrder = tokenSystem.taxonomyOrder;
|
|
66
|
+
if (!Array.isArray(taxonomyOrder) || taxonomyOrder.join('|') !== 'primitive|semantic|component') {
|
|
67
|
+
validationErrors.push('designIntent.tokenSystem.taxonomyOrder must equal ["primitive","semantic","component"].');
|
|
68
|
+
}
|
|
69
|
+
if (tokenSystem.primitiveColorSpace !== 'OKLCH') {
|
|
70
|
+
validationErrors.push('designIntent.tokenSystem.primitiveColorSpace must equal "OKLCH".');
|
|
71
|
+
}
|
|
72
|
+
if (tokenSystem.requireSemanticAliases !== true) {
|
|
73
|
+
validationErrors.push('designIntent.tokenSystem.requireSemanticAliases must equal true.');
|
|
74
|
+
}
|
|
75
|
+
if (tokenSystem.semanticAliasesMutableWithoutComponentRewrite !== true) {
|
|
76
|
+
validationErrors.push('designIntent.tokenSystem.semanticAliasesMutableWithoutComponentRewrite must equal true.');
|
|
77
|
+
}
|
|
78
|
+
if (tokenSystem.componentTokensConsumeSemantic !== true) {
|
|
79
|
+
validationErrors.push('designIntent.tokenSystem.componentTokensConsumeSemantic must equal true.');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
validateTokenLockingPolicy(tokenSystem, validationErrors);
|
|
83
|
+
validateTokenFallbackAndNaming(tokenSystem, validationErrors);
|
|
84
|
+
return validationErrors;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function validateColorTruth(designIntentContract, validationErrors) {
|
|
88
|
+
if (!designIntentContract.colorTruth || typeof designIntentContract.colorTruth !== 'object') {
|
|
89
|
+
validationErrors.push('designIntent.colorTruth must exist.');
|
|
90
|
+
return validationErrors;
|
|
91
|
+
}
|
|
92
|
+
const colorTruth = designIntentContract.colorTruth;
|
|
93
|
+
if (colorTruth.format !== 'OKLCH') {
|
|
94
|
+
validationErrors.push('designIntent.colorTruth.format must equal "OKLCH".');
|
|
95
|
+
}
|
|
96
|
+
if (colorTruth.allowHexDerivatives !== true) {
|
|
97
|
+
validationErrors.push('designIntent.colorTruth.allowHexDerivatives must equal true.');
|
|
98
|
+
}
|
|
99
|
+
if (!String(colorTruth.intent || '').trim()) {
|
|
100
|
+
validationErrors.push('designIntent.colorTruth.intent must be a non-empty string.');
|
|
101
|
+
}
|
|
102
|
+
const paletteRoles = colorTruth.paletteRoles;
|
|
103
|
+
if (!Array.isArray(paletteRoles) || paletteRoles.length < 1) {
|
|
104
|
+
validationErrors.push('designIntent.colorTruth.paletteRoles must define or request agent-defined semantic palette roles.');
|
|
105
|
+
}
|
|
106
|
+
if (colorTruth.rolesMustBeAgentDefined !== true) {
|
|
107
|
+
validationErrors.push('designIntent.colorTruth.rolesMustBeAgentDefined must equal true.');
|
|
108
|
+
}
|
|
109
|
+
return validationErrors;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function validateCrossViewportAdaptation(designIntentContract, validationErrors) {
|
|
113
|
+
if (!designIntentContract.crossViewportAdaptation || typeof designIntentContract.crossViewportAdaptation !== 'object') {
|
|
114
|
+
validationErrors.push('designIntent.crossViewportAdaptation must exist.');
|
|
115
|
+
return validationErrors;
|
|
116
|
+
}
|
|
117
|
+
const mutationRules = designIntentContract.crossViewportAdaptation.mutationRules;
|
|
118
|
+
if (!mutationRules || typeof mutationRules !== 'object') {
|
|
119
|
+
validationErrors.push('designIntent.crossViewportAdaptation.mutationRules must exist.');
|
|
120
|
+
return validationErrors;
|
|
121
|
+
}
|
|
122
|
+
for (const viewportKey of ['mobile', 'tablet', 'desktop']) {
|
|
123
|
+
if (!String(mutationRules[viewportKey] || '').trim()) {
|
|
124
|
+
validationErrors.push(`designIntent.crossViewportAdaptation.mutationRules.${viewportKey} must be a non-empty string.`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return validationErrors;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function validateMotionSystem(designIntentContract, validationErrors) {
|
|
131
|
+
if (!designIntentContract.motionSystem || typeof designIntentContract.motionSystem !== 'object') {
|
|
132
|
+
validationErrors.push('designIntent.motionSystem must exist.');
|
|
133
|
+
return validationErrors;
|
|
134
|
+
}
|
|
135
|
+
if (designIntentContract.motionSystem.allowMeaningfulMotion !== true) {
|
|
136
|
+
validationErrors.push('designIntent.motionSystem.allowMeaningfulMotion must equal true.');
|
|
137
|
+
}
|
|
138
|
+
if (!String(designIntentContract.motionSystem.purpose || '').trim()) {
|
|
139
|
+
validationErrors.push('designIntent.motionSystem.purpose must be a non-empty string.');
|
|
140
|
+
}
|
|
141
|
+
if (designIntentContract.motionSystem.respectReducedMotion !== true) {
|
|
142
|
+
validationErrors.push('designIntent.motionSystem.respectReducedMotion must equal true.');
|
|
143
|
+
}
|
|
144
|
+
return validationErrors;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function validateComponentMorphology(designIntentContract, validationErrors) {
|
|
148
|
+
if (!designIntentContract.componentMorphology || typeof designIntentContract.componentMorphology !== 'object') {
|
|
149
|
+
validationErrors.push('designIntent.componentMorphology must exist.');
|
|
150
|
+
return validationErrors;
|
|
151
|
+
}
|
|
152
|
+
const componentMorphology = designIntentContract.componentMorphology;
|
|
153
|
+
if (componentMorphology.requireStateBehaviorMatrix !== true) {
|
|
154
|
+
validationErrors.push('designIntent.componentMorphology.requireStateBehaviorMatrix must equal true.');
|
|
155
|
+
}
|
|
156
|
+
if (!Array.isArray(componentMorphology.stateKeys) || componentMorphology.stateKeys.length < 4) {
|
|
157
|
+
validationErrors.push('designIntent.componentMorphology.stateKeys must contain multiple interaction states.');
|
|
158
|
+
}
|
|
159
|
+
const viewportBehavior = componentMorphology.viewportBehavior;
|
|
160
|
+
if (!viewportBehavior || typeof viewportBehavior !== 'object') {
|
|
161
|
+
validationErrors.push('designIntent.componentMorphology.viewportBehavior must exist.');
|
|
162
|
+
return validationErrors;
|
|
163
|
+
}
|
|
164
|
+
for (const viewportKey of ['mobile', 'tablet', 'desktop']) {
|
|
165
|
+
if (!String(viewportBehavior[viewportKey] || '').trim()) {
|
|
166
|
+
validationErrors.push(`designIntent.componentMorphology.viewportBehavior.${viewportKey} must be a non-empty string.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return validationErrors;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function validateAccessibilityPolicy(designIntentContract, validationErrors) {
|
|
173
|
+
if (!designIntentContract.accessibilityPolicy || typeof designIntentContract.accessibilityPolicy !== 'object') {
|
|
174
|
+
validationErrors.push('designIntent.accessibilityPolicy must exist.');
|
|
175
|
+
return validationErrors;
|
|
176
|
+
}
|
|
177
|
+
const accessibilityPolicy = designIntentContract.accessibilityPolicy;
|
|
178
|
+
if (accessibilityPolicy.hardComplianceFloor !== 'WCAG-2.2-AA') {
|
|
179
|
+
validationErrors.push('designIntent.accessibilityPolicy.hardComplianceFloor must equal "WCAG-2.2-AA".');
|
|
180
|
+
}
|
|
181
|
+
if (accessibilityPolicy.advisoryContrastModel !== 'APCA') {
|
|
182
|
+
validationErrors.push('designIntent.accessibilityPolicy.advisoryContrastModel must equal "APCA".');
|
|
183
|
+
}
|
|
184
|
+
if (accessibilityPolicy.failOnHardViolations !== true) {
|
|
185
|
+
validationErrors.push('designIntent.accessibilityPolicy.failOnHardViolations must equal true.');
|
|
186
|
+
}
|
|
187
|
+
if (accessibilityPolicy.advisoryFindingsDoNotBlockByDefault !== true) {
|
|
188
|
+
validationErrors.push('designIntent.accessibilityPolicy.advisoryFindingsDoNotBlockByDefault must equal true.');
|
|
189
|
+
}
|
|
190
|
+
const hardRequirements = accessibilityPolicy.hardRequirements;
|
|
191
|
+
if (!hardRequirements || typeof hardRequirements !== 'object') {
|
|
192
|
+
validationErrors.push('designIntent.accessibilityPolicy.hardRequirements must exist.');
|
|
193
|
+
} else {
|
|
194
|
+
for (const requirementKey of [
|
|
195
|
+
'textContrastMinimum',
|
|
196
|
+
'nonTextContrast',
|
|
197
|
+
'useOfColorOnlyProhibited',
|
|
198
|
+
'focusVisible',
|
|
199
|
+
'focusAppearance',
|
|
200
|
+
'targetSizeMinimum',
|
|
201
|
+
'keyboardAccess',
|
|
202
|
+
'reflowRequired',
|
|
203
|
+
'accessibleAuthenticationMinimum',
|
|
204
|
+
'statusMessagesAndDynamicStateAccess',
|
|
205
|
+
]) {
|
|
206
|
+
if (hardRequirements[requirementKey] !== true) {
|
|
207
|
+
validationErrors.push(`designIntent.accessibilityPolicy.hardRequirements.${requirementKey} must equal true.`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const advisoryChecks = accessibilityPolicy.advisoryChecks;
|
|
212
|
+
if (!advisoryChecks || typeof advisoryChecks !== 'object') {
|
|
213
|
+
validationErrors.push('designIntent.accessibilityPolicy.advisoryChecks must exist.');
|
|
214
|
+
return validationErrors;
|
|
215
|
+
}
|
|
216
|
+
for (const advisoryKey of [
|
|
217
|
+
'perceptualContrastReview',
|
|
218
|
+
'darkModeContrastTuning',
|
|
219
|
+
'typographyReadabilityTuning',
|
|
220
|
+
]) {
|
|
221
|
+
if (advisoryChecks[advisoryKey] !== true) {
|
|
222
|
+
validationErrors.push(`designIntent.accessibilityPolicy.advisoryChecks.${advisoryKey} must equal true.`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return validationErrors;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function validateContextHygiene(designIntentContract, validationErrors) {
|
|
229
|
+
if (!designIntentContract.contextHygiene || typeof designIntentContract.contextHygiene !== 'object') {
|
|
230
|
+
validationErrors.push('designIntent.contextHygiene must exist.');
|
|
231
|
+
return validationErrors;
|
|
232
|
+
}
|
|
233
|
+
const contextHygiene = designIntentContract.contextHygiene;
|
|
234
|
+
if (contextHygiene.continuityMode !== 'opt-in-only') {
|
|
235
|
+
validationErrors.push('designIntent.contextHygiene.continuityMode must equal "opt-in-only".');
|
|
236
|
+
}
|
|
237
|
+
if (contextHygiene.repoEvidenceOverridesMemory !== true) {
|
|
238
|
+
validationErrors.push('designIntent.contextHygiene.repoEvidenceOverridesMemory must equal true.');
|
|
239
|
+
}
|
|
240
|
+
if (contextHygiene.requireExplicitContinuityApproval !== true) {
|
|
241
|
+
validationErrors.push('designIntent.contextHygiene.requireExplicitContinuityApproval must equal true.');
|
|
242
|
+
}
|
|
243
|
+
if (contextHygiene.forbidCarryoverWhenUnapproved !== true) {
|
|
244
|
+
validationErrors.push('designIntent.contextHygiene.forbidCarryoverWhenUnapproved must equal true.');
|
|
245
|
+
}
|
|
246
|
+
if (!Array.isArray(contextHygiene.allowedSources) || contextHygiene.allowedSources.length < 4) {
|
|
247
|
+
validationErrors.push('designIntent.contextHygiene.allowedSources must list the approved design evidence sources.');
|
|
248
|
+
}
|
|
249
|
+
if (!Array.isArray(contextHygiene.taintedSources) || contextHygiene.taintedSources.length < 3) {
|
|
250
|
+
validationErrors.push('designIntent.contextHygiene.taintedSources must list tainted carryover sources.');
|
|
251
|
+
}
|
|
252
|
+
if (!String(contextHygiene.approvedExternalConstraintUsage || '').trim()) {
|
|
253
|
+
validationErrors.push('designIntent.contextHygiene.approvedExternalConstraintUsage must be a non-empty string.');
|
|
254
|
+
}
|
|
255
|
+
return validationErrors;
|
|
256
|
+
}
|